Skip to content

Instantly share code, notes, and snippets.

@loiclefloch
Created February 23, 2016 13:30
Show Gist options
  • Save loiclefloch/20be5ad39a74c912e030 to your computer and use it in GitHub Desktop.
Save loiclefloch/20be5ad39a74c912e030 to your computer and use it in GitHub Desktop.
Android tools
import java.util.List;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.http.Body;
import retrofit.http.GET;
import retrofit.http.POST;
import retrofit.http.PUT;
import retrofit.http.Path;
public class ApiManager {
private static ApiManager instance;
public BookmarkService bookmarkService;
public TagService tagService;
RestAdapter catalogRestAdapter;
public static ApiManager getInstance() {
if (instance == null) {
instance = new ApiManager();
}
return instance;
}
public ApiManager() {
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader(Constants.api.HEADER_API_KEY, Constants.api.API_TEST_KEY);
}
};
catalogRestAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.api.URL)
.setRequestInterceptor(requestInterceptor)
.setLogLevel((BuildConfig.DEBUG ?
RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE))
.build();
bookmarkService = catalogRestAdapter.create(BookmarkService.class);
tagService = catalogRestAdapter.create(TagService.class);
}
public void getBookmarks(RestCallback<List<Bookmark>> callback) {
bookmarkService.get(callback);
}
public void postBookmark(Bookmark bookmark, RestCallback<Bookmark> callback) {
bookmarkService.post(bookmark, callback);
}
public void putBookmark(Bookmark bookmark, RestCallback<Bookmark> callback) {
bookmarkService.put(bookmark.getId(), bookmark, callback);
}
public void getTags(RestCallback<List<Tag>> callback) {
tagService.get(callback);
}
// ----------- Services.
public interface BookmarkService {
// -- The api errors code
int BOOKMARK_URL_EMPTY = 4001;
int BOOKMARK_ALREADY_EXISTS = 4002;
int INVALID_URL = 4002;
int WEBSITE_NO_TITLE = 4003;
int MONGODB_ERROR = 5001; // Failed to save the bookmark
int BOOKMARK_NOT_FOUND = 1002;
// -- The routes
@GET("/bookmark")
void get(RestCallback<List<Bookmark>> callback);
@POST("/bookmark")
void post(@Body Bookmark bookmark, RestCallback<Bookmark> callback);
@PUT("/bookmark/{id}")
void put(@Path("id") String id, @Body Bookmark bookmark, RestCallback<Bookmark> callback);
}
public interface TagService {
// -- The api errors code
// TODO
// -- The routes
@GET("/tag")
void get(RestCallback<List<Tag>> callback);
@POST("/tag")
void post(@Body Tag bookmark, RestCallback<Tag> callback);
@PUT("/tag")
void put(@Body Tag bookmark, RestCallback<Tag> callback);
}
}
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import butterknife.ButterKnife;
public class BaseAppCompatActivity extends AppCompatActivity
implements ViewInterface {
ProgressDialog loadingDialog;
View parentLayout;
protected android.support.v7.app.ActionBar toolbar;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initProgressDialog();
}
private void initProgressDialog() {
loadingDialog = new ProgressDialog(this);
loadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loadingDialog.setMessage(getText(R.string.loading));
loadingDialog.setCancelable(false);
}
/**
* Setup the toolbar
* @param title the title of the view.
*/
protected void initToolbar(String title) {
toolbar = getSupportActionBar();
if (toolbar != null) {
setToolbarTitle(title);
}
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
/**
* Remove the back icon on the toolbar. <br>
* To disable back button, override onBackPressed function to do nothing.
*/
protected void removeBackIconOnToolbar() {
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
protected void initView(int id, int parentLayoutId) {
setContentView(id);
this.parentLayout = findViewById(parentLayoutId);
ButterKnife.bind(this);
}
// --- View interface
@Override
public void displayError(String message) {
if (parentLayout != null) {
ViewTools.displayError(parentLayout, message);
}
}
@Override
public void displayToast(String title) {
ViewTools.displayToast(this, title);
}
@Override
public void showLoading() {
loadingDialog.show();
}
@Override
public void hideLoading() {
loadingDialog.hide();
}
@Override
public void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(
Context.INPUT_METHOD_SERVICE);
View currentFocus = getCurrentFocus();
if (currentFocus != null) {
imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
}
}
@Override
public void setToolbarTitle(String title) {
toolbar.setTitle(title);
}
}
import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MenuActivity extends BaseAppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
public enum Screen {
BOOKMARKS,
TAGS,
SETTINGS,
PROFILE
}
public static class FragmentInfo {
public String name;
public String fragmentName;
FragmentInfo(String name, String classPath) {
this.name = name;
this.fragmentName = classPath;
}
}
private static final Map<Screen, FragmentInfo> fragments = Collections.unmodifiableMap(
new HashMap<Screen, FragmentInfo>() {{
put(Screen.BOOKMARKS, new FragmentInfo("Bookmarks", "bm.bookmark_manager.view.bm_list.BmListView"));
put(Screen.TAGS, new FragmentInfo("Tags", "bm.bookmark_manager.view.tag_list.TagListView"));
}});
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView(R.layout.activity__main, R.id.nav_view);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
// Open home (bookmark list)
openFragment(Screen.BOOKMARKS);
navigationView.getMenu().getItem(0).setChecked(true);
}
@Override
protected void onResume() {
super.onResume();
hideKeyboard();
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_home) {
openFragment(Screen.BOOKMARKS);
} else if (id == R.id.nav_tags) {
openFragment(Screen.TAGS);
} else if (id == R.id.nav_settings) {
} else if (id == R.id.nav_logout) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
private void openFragment(Screen screen) {
FragmentInfo fragmentInfo = fragments.get(screen);
toolbar.setTitle(fragmentInfo.name);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(fragmentInfo.fragmentName);
if (fragment == null) {
fragment = Fragment.instantiate(this, fragmentInfo.fragmentName);
}
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.main, fragment, fragmentInfo.fragmentName);
tx.commit();
}
}
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.orhanobut.logger.Logger;
import java.io.Serializable;
public abstract class Model implements Serializable {
protected abstract String getClassName();
@Override
public String toString() {
Gson gson = new GsonBuilder().create();
String json = gson.toJson(this);
if (TextUtils.isEmpty(getClassName())) {
return json;
}
return getClassName() + ": " + json;
}
private void dump() {
Logger.i(String.valueOf(this));
}
public <T extends Model> T copy(Class<T> type) {
Gson gson = new GsonBuilder().create();
return gson.fromJson(gson.toJson(this), type);
}
}
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;
/**
* Tools to print notifications to the user
* - Toast and snackbar will never appear if the notifications are disabled for the app
* - AlertDialog appears
* So we check if the notifications are disabled. If it is, we use an AlertDialog with juste a “ok” button.
*/
public final class Notification {
/**
* Display a toast if possible, an alert dialog otherwise
* @param context The current context
* @param message The message to display
*/
public static void show(Context context, String message) {
if (PermissionUtil.isNotificationEnabled(context)) {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
else {
showSimpleAlertDialog(context, message);
}
}
/**
* Display a toast with custom gravity and position if possible, an alert dialog otherwise
* @param context The current context
* @param message The message to display
*/
public static void showWithGravity(Context context, String message, int gravity, int xOffset, int yOffset) {
if (PermissionUtil.isNotificationEnabled(context)) {
Toast toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.setGravity(gravity, xOffset, yOffset);
toast.show();
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
else {
showSimpleAlertDialog(context, message);
}
}
/**
* Display an alert dialog with only an "ok" button
* @param context The current context
* @param message The message to display
*/
private static void showSimpleAlertDialog(Context context, String message) {
try {
new AlertDialog.Builder(context)
.setTitle("")
.setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
} catch (Exception e) {
e.printStackTrace();
// This exception occurred if the context is not good (do not use getApplicationContext() but Class.this)
// Also, the activity must have android:theme="@style/AppTheme" defined on the AndroidManifest
if (Constants.DEV_MODE) {
throw new AssertionError("Invalid context to display the alert dialog");
}
}
}
}
public interface PermissionCallback {
void retry();
void refused();
}
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Utility class that wraps access to the runtime permissions API in M and provides basic helper
* methods.
*/
public abstract class PermissionUtil {
/**
* Id to identify a permission setting request
*/
public static final int REQUEST_PERMISSION_SETTING = 90;
/**
* Check that all given permissions have been granted by verifying that each entry in the
* given array is of the value {@link PackageManager#PERMISSION_GRANTED}.
*
* @see Activity#onRequestPermissionsResult(int, String[], int[])
*/
public static boolean verifyPermissions(int[] grantResults) {
// At least one result must be checked.
if (grantResults.length < 1) {
return false;
}
// Verify that each required permission has been granted, otherwise return false.
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* Call this to tell the user why we need the permission, and redirect him to the settings if
* click on "Ok"
*
* @param activity the current activity
* @param text the text to explain why we need this permission (begin with without_permission_)
*/
public static void displayPermissionTotallyRefusedAlert(final Activity activity, String text) {
AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
alertDialog.setTitle(R.string.permission_required);
alertDialog.setMessage(text);
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
openSettings(activity);
}
});
alertDialog.show();
}
/**
* Call this to give an explanation about the permission asked after the user refuse it.<br>
* It will ask the user if he wants to have the possibility to give the permission ("Retry")
* or not ("I am sure")
*
* @param activity the current activity
* @param text the text to display (begin with need_permission_). We add "ask_if_sure_to_deny_permission" question text at the end.
* @param callback the PermissionCallback.<br>
* retry() is called when the user wants to have the possibility to give the permission.<br>
* refused() is called when the user does not want to give the permission at all.
*/
public static void displayPermissionInfoMessage(Activity activity, String text, final PermissionCallback callback) {
AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
alertDialog.setTitle(R.string.error);
alertDialog.setMessage(text + " " + activity.getString(R.string.ask_if_sure_to_deny_permission));
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.retry),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
callback.retry();
}
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, activity.getString(R.string.i_am_sure),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
callback.refused();
}
});
alertDialog.show();
}
/**
* Create and start an intent to redirect the user to the app settings. The user need to go to the "Permissions" section
* and enable the asked permissions.
*
* @param activity
*/
public static void openSettings(Activity activity) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
}
public static void displayLocationSettingsAlert(final Activity activity) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(activity);
alertDialog.setTitle(activity.getBaseContext().getText(R.string.near_resto_popup_gps_title));
alertDialog.setMessage(activity.getBaseContext().getText(R.string.near_resto_popup_gps_content));
alertDialog.setPositiveButton(activity.getBaseContext().getText(R.string.settings), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
activity.startActivity(intent);
dialog.cancel();
}
});
alertDialog.setNegativeButton(activity.getBaseContext().getText(R.string.cancel), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alertDialog.show();
}
private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";
/**
* !!! Use this function only for notificaitons like toast and snackbar. Because below
* KITKAT, it will always return false to force use a custom view instead the toast.
* @param context the current context.
* @return true if the notifications are enabled and we can display a toast.
*/
public static boolean isNotificationEnabled(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
//Source: http://stackoverflow.com/questions/11649151/android-4-1-how-to-check-notifications-are-disabled-for-the-application/30108004#30108004
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getApplicationContext().getPackageName();
int uid = appInfo.uid;
Class appOpsClass = null; /* Context.APP_OPS_MANAGER */
try {
appOpsClass = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);
Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
int value = (int) opPostNotificationValue.get(Integer.class);
return ((int) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg) == AppOpsManager.MODE_ALLOWED);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// We don't have any way to found if notification are enable before KITKAT. So we consider it's not.
return false;
}
}
import com.google.gson.Gson;
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
import retrofit.mime.TypedByteArray;
/**
* Override callback to get a simple error
* Override callback to get a simple error
* @param <T>
*/
public abstract class RestCallback<T> implements Callback<T>
{
// -- We need to create those functions each time we create a RestCallback
public abstract void success(T t);
public abstract void failure(RestError restError);
@Override
public void success(T t, Response response) {
success(t);
}
@Override
public void failure(RetrofitError error)
{
// Try to unserialize the body
RestError restError = null;
try {
if (error.getResponse() != null) {
restError = (RestError) error.getBodyAs(RestError.class);
if (restError == null || restError.getCode() == null) {
TypedByteArray jsonBody = (TypedByteArray)error.getResponse().getBody();
if (jsonBody != null) {
restError = new Gson().fromJson(new String(jsonBody.getBytes()), RestError.class);
}
}
}
}
catch (RuntimeException e) {
e.printStackTrace();
}
// it's not a valid Api error with a correct json.
if (restError == null) {
restError = new RestError();
}
// We save the RetrofitError
restError.setError(error);
failure(restError);
}
}
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.Objects;
import retrofit.RetrofitError;
/**
* RestError represent an error from the API.
*/
public class RestError
{
private static final Integer DEFAULT_ERROR_CODE = -1;
/**
* The api error code or the http status if the json can't be unserialize.
*/
@SerializedName("code")
private Integer code = DEFAULT_ERROR_CODE;
/**
* The api error message
*/
@SerializedName("message")
private String message = "";
/**
* The retrofit error
*/
@Expose
private RetrofitError error;
/**
* Return if the code contains an api error code or and http error code
* @return boolean
*/
public boolean isApiError() {
return this.code != null && !Objects.equals(this.code, DEFAULT_ERROR_CODE);
}
public retrofit.client.Response getResponse() {
return error.getResponse();
}
public int getStatusCode() {
return error.getResponse().getStatus();
}
@Override
public String toString() {
return "ResError\n"
+ "[" + code + "] " + message + "\n"
+ error;
}
// -- getters and setters
public String getMessage() {
return message;
}
public void setMessage(String strMessage) {
this.message = strMessage;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public RetrofitError getError() {
return error;
}
public void setError(RetrofitError error) {
this.error = error;
}
}
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
public class SecurePreferences {
public static class SecurePreferencesException extends RuntimeException {
public SecurePreferencesException(Throwable e) {
super(e);
}
}
private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
private static final String KEY_TRANSFORMATION = "AES/ECB/PKCS5Padding";
private static final String SECRET_KEY_HASH_TRANSFORMATION = "SHA-256";
private static final String CHARSET = "UTF-8";
private final boolean encryptKeys;
private final Cipher writer;
private final Cipher reader;
private final Cipher keyWriter;
private final SharedPreferences preferences;
/**
* This will initialize an instance of the SecurePreferences class
*
* @param context
* your current context.
* @param preferenceName
* name of preferences file (preferenceName.xml)
* @param secureKey
* the key used for encryption, finding a good key scheme is hard. Hardcoding your key in the
* application is bad, but better than plaintext preferences. Having the user enter the key
* upon application launch is a safe(r) alternative, but annoying to the user.
* @param encryptKeys
* settings this to false will only encrypt the values, true will encrypt both values and gcm.
* Keys can contain a lot of information about the plaintext value of the value which can be
* used to decipher the value.
* @throws SecurePreferencesException
*/
public SecurePreferences(Context context, String preferenceName, String secureKey, boolean encryptKeys)
throws SecurePreferencesException {
try {
this.writer = Cipher.getInstance(TRANSFORMATION);
this.reader = Cipher.getInstance(TRANSFORMATION);
this.keyWriter = Cipher.getInstance(KEY_TRANSFORMATION);
initCiphers(secureKey);
this.preferences = context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE);
this.encryptKeys = encryptKeys;
}
catch (GeneralSecurityException | UnsupportedEncodingException e) {
throw new SecurePreferencesException(e);
}
}
private void initCiphers(String secureKey) throws UnsupportedEncodingException,
NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
IvParameterSpec ivSpec = getIv();
SecretKeySpec secretKey = getSecretKey(secureKey);
writer.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
reader.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
keyWriter.init(Cipher.ENCRYPT_MODE, secretKey);
}
private IvParameterSpec getIv() {
byte[] iv = new byte[writer.getBlockSize()];
System.arraycopy("fldsjfodasjifudslfjdsaofshaufihadsf".getBytes(), 0, iv, 0, writer.getBlockSize());
return new IvParameterSpec(iv);
}
private SecretKeySpec getSecretKey(String key) throws UnsupportedEncodingException,
NoSuchAlgorithmException {
byte[] keyBytes = createKeyBytes(key);
return new SecretKeySpec(keyBytes, TRANSFORMATION);
}
private byte[] createKeyBytes(String key) throws UnsupportedEncodingException, NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(SECRET_KEY_HASH_TRANSFORMATION);
md.reset();
return md.digest(key.getBytes(CHARSET));
}
public void put(String key, String value) {
if (value == null) {
preferences.edit().remove(toKey(key)).apply();
} else {
putValue(toKey(key), value);
}
}
public boolean containsKey(String key) {
return preferences.contains(toKey(key));
}
public void removeValue(String key) {
preferences.edit().remove(toKey(key)).apply();
}
public String getString(String key) throws SecurePreferencesException {
if (preferences.contains(toKey(key))) {
String securedEncodedValue = preferences.getString(toKey(key), "");
return decrypt(securedEncodedValue);
}
return null;
}
public void clear() {
preferences.edit().clear().apply();
}
private String toKey(String key) {
if (encryptKeys)
return encrypt(key, keyWriter);
else
return key;
}
private void putValue(String key, String value) throws SecurePreferencesException {
String secureValueEncoded = encrypt(value, writer);
preferences.edit().putString(key, secureValueEncoded).apply();
}
private String encrypt(String value, Cipher writer) throws SecurePreferencesException {
byte[] secureValue;
try {
secureValue = convert(writer, value.getBytes(CHARSET));
} catch (UnsupportedEncodingException e) {
throw new SecurePreferencesException(e);
}
return Base64.encodeToString(secureValue, Base64.NO_WRAP);
}
private String decrypt(String securedEncodedValue) {
byte[] securedValue = Base64.decode(securedEncodedValue, Base64.NO_WRAP);
byte[] value = convert(reader, securedValue);
try {
return new String(value, CHARSET);
} catch (UnsupportedEncodingException e) {
throw new SecurePreferencesException(e);
}
}
private static byte[] convert(Cipher cipher, byte[] bs) throws SecurePreferencesException {
try {
return cipher.doFinal(bs);
} catch (Exception e) {
throw new SecurePreferencesException(e);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment