Refactoring

Decouple SIA from other stuff.
Move SIA-related initialization to a separate class.
This commit is contained in:
xynngh
2020-05-14 19:20:19 +04:00
parent 10e39a4435
commit 930372f488
24 changed files with 462 additions and 218 deletions

View File

@@ -4,8 +4,7 @@ import android.app.Application;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
import dummydomain.yetanothercallblocker.data.ContactsHelper; import dummydomain.yetanothercallblocker.data.Config;
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
public class App extends Application { public class App extends Application {
@@ -31,8 +30,7 @@ public class App extends Application {
NotificationHelper.createNotificationChannels(this); NotificationHelper.createNotificationChannels(this);
DatabaseSingleton.setContactsProvider( Config.init(this, settings);
ContactsHelper.getContactsProvider(this, settings));
} }
public static App getInstance() { public static App getInstance() {

View File

@@ -0,0 +1,46 @@
package dummydomain.yetanothercallblocker;
import android.content.Context;
import android.content.SharedPreferences;
public class GenericSettings {
protected final SharedPreferences pref;
public GenericSettings(Context context, String name) {
this(context.getSharedPreferences(name, Context.MODE_PRIVATE));
}
public GenericSettings(SharedPreferences pref) {
this.pref = pref;
}
public boolean getBoolean(String key) {
return getBoolean(key, false);
}
public boolean getBoolean(String key, boolean defValue) {
return pref.getBoolean(key, defValue);
}
public void setBoolean(String key, boolean value) {
pref.edit().putBoolean(key, value).apply();
}
public int getInt(String key, int defValue) {
return pref.getInt(key, defValue);
}
public void setInt(String key, int value) {
pref.edit().putInt(key, value).apply();
}
public long getLong(String key, long defValue) {
return pref.getLong(key, defValue);
}
public void setLong(String key, long value) {
pref.edit().putLong(key, value).apply();
}
}

View File

@@ -15,8 +15,8 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.List; import java.util.List;
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.model.CommunityReview; import dummydomain.yetanothercallblocker.sia.model.CommunityReview;
import dummydomain.yetanothercallblocker.sia.model.CommunityReviewsLoader;
public class ReviewsActivity extends AppCompatActivity { public class ReviewsActivity extends AppCompatActivity {
@@ -59,7 +59,7 @@ public class ReviewsActivity extends AppCompatActivity {
= new AsyncTask<Void, Void, List<CommunityReview>>() { = new AsyncTask<Void, Void, List<CommunityReview>>() {
@Override @Override
protected List<CommunityReview> doInBackground(Void... voids) { protected List<CommunityReview> doInBackground(Void... voids) {
return CommunityReviewsLoader.loadReviews(paramNumber); return DatabaseSingleton.getCommunityReviewsLoader().loadReviews(paramNumber);
} }
@Override @Override

View File

@@ -1,9 +1,8 @@
package dummydomain.yetanothercallblocker; package dummydomain.yetanothercallblocker;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
public class Settings { public class Settings extends GenericSettings {
private static final String PREF_INCOMING_CALL_NOTIFICATIONS = "incomingCallNotifications"; private static final String PREF_INCOMING_CALL_NOTIFICATIONS = "incomingCallNotifications";
private static final String PREF_BLOCK_CALLS = "blockCalls"; private static final String PREF_BLOCK_CALLS = "blockCalls";
@@ -11,10 +10,8 @@ public class Settings {
private static final String PREF_LAST_UPDATE_TIME = "lastUpdateTime"; private static final String PREF_LAST_UPDATE_TIME = "lastUpdateTime";
private static final String PREF_LAST_UPDATE_CHECK_TIME = "lastUpdateCheckTime"; private static final String PREF_LAST_UPDATE_CHECK_TIME = "lastUpdateCheckTime";
private final SharedPreferences pref;
Settings(Context context) { Settings(Context context) {
pref = context.getSharedPreferences("yacb_preferences", Context.MODE_PRIVATE); super(context, "yacb_preferences");
} }
public boolean getIncomingCallNotifications() { public boolean getIncomingCallNotifications() {
@@ -57,24 +54,4 @@ public class Settings {
setLong(PREF_LAST_UPDATE_CHECK_TIME, timestamp); setLong(PREF_LAST_UPDATE_CHECK_TIME, timestamp);
} }
public boolean getBoolean(String key) {
return getBoolean(key, false);
}
public boolean getBoolean(String key, boolean defValue) {
return pref.getBoolean(key, defValue);
}
public void setBoolean(String key, boolean value) {
pref.edit().putBoolean(key, value).apply();
}
public long getLong(String key, long defValue) {
return pref.getLong(key, defValue);
}
public void setLong(String key, long value) {
pref.edit().putLong(key, value).apply();
}
} }

View File

@@ -0,0 +1,14 @@
package dummydomain.yetanothercallblocker.data;
import android.content.Context;
import dummydomain.yetanothercallblocker.GenericSettings;
import dummydomain.yetanothercallblocker.sia.Properties;
public class AndroidProperties extends GenericSettings implements Properties {
public AndroidProperties(Context context, String name) {
super(context, name);
}
}

View File

@@ -0,0 +1,38 @@
package dummydomain.yetanothercallblocker.data;
import android.content.Context;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import dummydomain.yetanothercallblocker.sia.Storage;
public class AndroidStorage implements Storage {
private final Context context;
public AndroidStorage(Context context) {
this.context = context;
}
@Override
public String getDataDirPath() {
return context.getFilesDir().getAbsolutePath() + "/";
}
@Override
public String getCacheDirPath() {
return context.getCacheDir().getAbsolutePath() + "/";
}
@Override
public InputStream openFile(String fileName, boolean internal) throws IOException {
if (internal) {
return context.getAssets().open(fileName);
} else {
return new FileInputStream(getDataDirPath() + fileName);
}
}
}

View File

@@ -0,0 +1,78 @@
package dummydomain.yetanothercallblocker.data;
import android.content.Context;
import android.text.TextUtils;
import dummydomain.yetanothercallblocker.PermissionHelper;
import dummydomain.yetanothercallblocker.sia.Settings;
import dummydomain.yetanothercallblocker.sia.SettingsImpl;
import dummydomain.yetanothercallblocker.sia.Storage;
import dummydomain.yetanothercallblocker.sia.model.CommunityReviewsLoader;
import dummydomain.yetanothercallblocker.sia.model.database.AbstractDatabase;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabase;
import dummydomain.yetanothercallblocker.sia.network.WebService;
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_PATH_PREFIX;
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_PROPERTIES;
import static dummydomain.yetanothercallblocker.data.SiaConstants.SIA_SECONDARY_PATH_PREFIX;
public class Config {
private static class WSParameterProvider extends WebService.DefaultWSParameterProvider {
CommunityDatabase communityDatabase;
void setCommunityDatabase(CommunityDatabase communityDatabase) {
this.communityDatabase = communityDatabase;
}
@Override
public String getAppId() {
return "qQq0O9nCRNy_aVdPgU9WOA";
}
@Override
public int getAppVersion() {
return communityDatabase.getSiaAppVersion();
}
@Override
public int getDbVersion() {
return communityDatabase.getEffectiveDbVersion();
}
}
public static void init(Context context, dummydomain.yetanothercallblocker.Settings settings) {
Storage storage = new AndroidStorage(context);
Settings siaSettings
= new SettingsImpl(new AndroidProperties(context, SIA_PROPERTIES));
WSParameterProvider wsParameterProvider = new WSParameterProvider();
WebService webService = new WebService(wsParameterProvider);
DatabaseSingleton.setDbManager(new DbManager(storage, SIA_PATH_PREFIX));
CommunityDatabase communityDatabase = new CommunityDatabase(
storage, AbstractDatabase.Source.ANY, SIA_PATH_PREFIX,
SIA_SECONDARY_PATH_PREFIX, siaSettings, webService);
wsParameterProvider.setCommunityDatabase(communityDatabase);
DatabaseSingleton.setCommunityDatabase(communityDatabase);
DatabaseSingleton.setFeaturedDatabase(new FeaturedDatabase(
storage, AbstractDatabase.Source.ANY, SIA_PATH_PREFIX));
DatabaseSingleton.setCommunityReviewsLoader(
new CommunityReviewsLoader(webService, "US"));
DatabaseSingleton.setContactsProvider(number -> {
if (settings.getUseContacts() && PermissionHelper.hasContactsPermission(context)) {
String contactName = ContactsHelper.getContactName(context, number);
if (!TextUtils.isEmpty(contactName)) return new ContactItem(contactName);
}
return null;
});
}
}

View File

@@ -5,10 +5,6 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.text.TextUtils;
import dummydomain.yetanothercallblocker.PermissionHelper;
import dummydomain.yetanothercallblocker.Settings;
public class ContactsHelper { public class ContactsHelper {
@@ -16,16 +12,6 @@ public class ContactsHelper {
ContactsContract.PhoneLookup.DISPLAY_NAME ContactsContract.PhoneLookup.DISPLAY_NAME
}; };
public static ContactsProvider getContactsProvider(Context context, Settings settings) {
return number -> {
if (settings.getUseContacts() && PermissionHelper.hasContactsPermission(context)) {
String contactName = getContactName(context, number);
if (!TextUtils.isEmpty(contactName)) return new ContactItem(contactName);
}
return null;
};
}
public static String getContactName(Context context, String number) { public static String getContactName(Context context, String number) {
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(number)); Uri.encode(number));

View File

@@ -5,33 +5,61 @@ import android.text.TextUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import dummydomain.yetanothercallblocker.sia.model.CommunityReviewsLoader;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase; import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem; import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabaseItem;
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabase; import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabase;
import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabaseItem; import dummydomain.yetanothercallblocker.sia.model.database.FeaturedDatabaseItem;
import static dummydomain.yetanothercallblocker.sia.SiaConstants.SIA_PATH_PREFIX; public class DatabaseSingleton {
import static dummydomain.yetanothercallblocker.sia.SiaConstants.SIA_SECONDARY_PATH_PREFIX;
public class DatabaseSingleton { // TODO: rewrite
private static final CommunityDatabase COMMUNITY_DATABASE = new CommunityDatabase(SIA_PATH_PREFIX, SIA_SECONDARY_PATH_PREFIX);
private static final FeaturedDatabase FEATURED_DATABASE = new FeaturedDatabase(SIA_PATH_PREFIX);
private static final Logger LOG = LoggerFactory.getLogger(DatabaseSingleton.class); private static final Logger LOG = LoggerFactory.getLogger(DatabaseSingleton.class);
private static DbManager dbManager;
private static CommunityDatabase communityDatabase;
private static FeaturedDatabase featuredDatabase;
private static CommunityReviewsLoader communityReviewsLoader;
private static ContactsProvider contactsProvider; private static ContactsProvider contactsProvider;
static void setDbManager(DbManager dbManager) {
DatabaseSingleton.dbManager = dbManager;
}
static void setCommunityDatabase(CommunityDatabase communityDatabase) {
DatabaseSingleton.communityDatabase = communityDatabase;
}
static void setFeaturedDatabase(FeaturedDatabase featuredDatabase) {
DatabaseSingleton.featuredDatabase = featuredDatabase;
}
static void setCommunityReviewsLoader(CommunityReviewsLoader communityReviewsLoader) {
DatabaseSingleton.communityReviewsLoader = communityReviewsLoader;
}
static void setContactsProvider(ContactsProvider contactsProvider) {
DatabaseSingleton.contactsProvider = contactsProvider;
}
public static DbManager getDbManager() {
return dbManager;
}
public static CommunityDatabase getCommunityDatabase() { public static CommunityDatabase getCommunityDatabase() {
return COMMUNITY_DATABASE; return communityDatabase;
} }
public static FeaturedDatabase getFeaturedDatabase() { public static FeaturedDatabase getFeaturedDatabase() {
return FEATURED_DATABASE; return featuredDatabase;
} }
public static void setContactsProvider(ContactsProvider contactsProvider) { public static CommunityReviewsLoader getCommunityReviewsLoader() {
DatabaseSingleton.contactsProvider = contactsProvider; return communityReviewsLoader;
} }
public static NumberInfo getNumberInfo(String number) { public static NumberInfo getNumberInfo(String number) {
@@ -46,7 +74,7 @@ public class DatabaseSingleton { // TODO: rewrite
.getDbItemByNumber(number); .getDbItemByNumber(number);
LOG.trace("getNumberInfo() featuredItem={}", featuredItem); LOG.trace("getNumberInfo() featuredItem={}", featuredItem);
ContactItem contactItem = contactsProvider != null ? contactsProvider.get(number) : null; ContactItem contactItem = DatabaseSingleton.contactsProvider.get(number);
LOG.trace("getNumberInfo() contactItem={}", contactItem); LOG.trace("getNumberInfo() contactItem={}", contactItem);
NumberInfo numberInfo = new NumberInfo(); NumberInfo numberInfo = new NumberInfo();

View File

@@ -1,7 +1,8 @@
package dummydomain.yetanothercallblocker.sia; package dummydomain.yetanothercallblocker.data;
public interface SiaConstants { public interface SiaConstants {
String SIA_PROPERTIES = "sia_preferences";
String SIA_PATH_PREFIX = "sia/"; String SIA_PATH_PREFIX = "sia/";
String SIA_SECONDARY_PATH_PREFIX = "sia-secondary/"; String SIA_SECONDARY_PATH_PREFIX = "sia-secondary/";

View File

@@ -0,0 +1,9 @@
package dummydomain.yetanothercallblocker.sia;
public interface Properties {
int getInt(String key, int defValue);
void setInt(String key, int value);
}

View File

@@ -0,0 +1,13 @@
package dummydomain.yetanothercallblocker.sia;
public interface Settings {
int getBaseDbVersion();
void setBaseDbVersion(int version);
int getSecondaryDbVersion();
void setSecondaryDbVersion(int version);
}

View File

@@ -0,0 +1,34 @@
package dummydomain.yetanothercallblocker.sia;
public class SettingsImpl implements Settings {
private static final String PREF_BASE_DB_VERSION = "baseDbVersion";
private static final String PREF_SECONDARY_DB_VERSION = "secondaryDbVersion";
private final Properties props;
public SettingsImpl(Properties props) {
this.props = props;
}
@Override
public int getBaseDbVersion() {
return props.getInt(PREF_BASE_DB_VERSION, 0);
}
@Override
public void setBaseDbVersion(int version) {
props.setInt(PREF_BASE_DB_VERSION, version);
}
@Override
public int getSecondaryDbVersion() {
return props.getInt(PREF_SECONDARY_DB_VERSION, 0);
}
@Override
public void setSecondaryDbVersion(int version) {
props.setInt(PREF_SECONDARY_DB_VERSION, version);
}
}

View File

@@ -1,33 +0,0 @@
package dummydomain.yetanothercallblocker.sia;
import android.content.Context;
import android.content.SharedPreferences;
public class SiaSettings {
private static final String PREF_BASE_DB_VERSION = "baseDbVersion";
private static final String PREF_SECONDARY_DB_VERSION = "secondaryDbVersion";
private final SharedPreferences pref;
public SiaSettings(Context context) {
pref = context.getSharedPreferences("sia_preferences", Context.MODE_PRIVATE);
}
public int getBaseDbVersion() {
return pref.getInt(PREF_BASE_DB_VERSION, 0);
}
public void setBaseDbVersion(int version) {
pref.edit().putInt(PREF_BASE_DB_VERSION, version).apply();
}
public int getSecondaryDbVersion() {
return pref.getInt(PREF_SECONDARY_DB_VERSION, 0);
}
public void setSecondaryDbVersion(int version) {
pref.edit().putInt(PREF_SECONDARY_DB_VERSION, version).apply();
}
}

View File

@@ -0,0 +1,14 @@
package dummydomain.yetanothercallblocker.sia;
import java.io.IOException;
import java.io.InputStream;
public interface Storage {
String getDataDirPath();
String getCacheDirPath();
InputStream openFile(String fileName, boolean internal) throws IOException;
}

View File

@@ -17,7 +17,15 @@ public class CommunityReviewsLoader {
private static final Logger LOG = LoggerFactory.getLogger(CommunityReviewsLoader.class); private static final Logger LOG = LoggerFactory.getLogger(CommunityReviewsLoader.class);
public static List<CommunityReview> loadReviews(String number) { private final WebService webService;
private final String country;
public CommunityReviewsLoader(WebService webService, String country) {
this.webService = webService;
this.country = country;
}
public List<CommunityReview> loadReviews(String number) {
LOG.debug("loadReviews({}) started", number); LOG.debug("loadReviews({}) started", number);
if (number.startsWith("+")) { if (number.startsWith("+")) {
@@ -26,9 +34,9 @@ public class CommunityReviewsLoader {
Map<String, String> params = new HashMap<>(); Map<String, String> params = new HashMap<>();
params.put("number", number); params.put("number", number);
params.put("country", "RU"); // TODO: fix: hardcode params.put("country", country);
WebService.WSResponse response = WebService.callForJson(WebService.getGetReviewsUrlPart(), params); WebService.WSResponse response = webService.callForJson(webService.getGetReviewsUrlPart(), params);
List<CommunityReview> reviews = new ArrayList<>(); List<CommunityReview> reviews = new ArrayList<>();

View File

@@ -11,15 +11,21 @@ import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import static dummydomain.yetanothercallblocker.sia.utils.FileUtils.openFile; import dummydomain.yetanothercallblocker.sia.Storage;
public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V> { public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V> {
public enum Source {
INTERNAL, EXTERNAL, ANY
}
private static final Logger LOG = LoggerFactory.getLogger(AbstractDatabase.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractDatabase.class);
protected final Storage storage;
protected final Source source;
protected final String pathPrefix; protected final String pathPrefix;
protected boolean useAssets; protected boolean useInternal;
protected int baseDatabaseVersion; protected int baseDatabaseVersion;
protected int numberOfItems; protected int numberOfItems;
@@ -31,7 +37,9 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
protected boolean loaded; protected boolean loaded;
protected boolean failedToLoad; protected boolean failedToLoad;
public AbstractDatabase(String pathPrefix) { public AbstractDatabase(Storage storage, Source source, String pathPrefix) {
this.storage = storage;
this.source = source;
this.pathPrefix = pathPrefix; this.pathPrefix = pathPrefix;
} }
@@ -72,12 +80,12 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
reset(); reset();
if (load(true)) { if ((source == Source.ANY || source == Source.INTERNAL) && load(true)) {
useAssets = true; useInternal = true;
loaded = true; loaded = true;
return true; return true;
} else if (load(false)) { } else if ((source == Source.ANY || source == Source.EXTERNAL) && load(false)) {
useAssets = false; useInternal = false;
loaded = true; loaded = true;
return true; return true;
} else { } else {
@@ -87,10 +95,10 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
} }
} }
protected boolean load(boolean useAssets) { protected boolean load(boolean useInternal) {
if (loadInfoData(useAssets) && loadSliceListData(useAssets)) { if (loadInfoData(useInternal) && loadSliceListData(useInternal)) {
LOG.info("load() loaded DB useAssets={}, baseDatabaseVersion={}, numberOfItems={}", LOG.info("load() loaded DB useInternal={}, baseDatabaseVersion={}, numberOfItems={}",
useAssets, this.baseDatabaseVersion, this.numberOfItems); useInternal, this.baseDatabaseVersion, this.numberOfItems);
return true; return true;
} }
return false; return false;
@@ -102,12 +110,12 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
protected abstract String getNamePrefix(); protected abstract String getNamePrefix();
protected boolean loadInfoData(boolean useAssets) { protected boolean loadInfoData(boolean useInternal) {
LOG.debug("loadInfoData() started; useAssets: {}", useAssets); LOG.debug("loadInfoData() started; useInternal: {}", useInternal);
String fileName = getPathPrefix() + getNamePrefix() + "info.dat"; String fileName = getPathPrefix() + getNamePrefix() + "info.dat";
try (InputStream is = openFile(fileName, useAssets); try (InputStream is = storage.openFile(fileName, useInternal);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) {
String headerString = bufferedReader.readLine(); String headerString = bufferedReader.readLine();
@@ -130,7 +138,7 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
LOG.debug("loadInfoData() loaded MDI, baseDatabaseVersion={}, numberOfItems={}", LOG.debug("loadInfoData() loaded MDI, baseDatabaseVersion={}, numberOfItems={}",
this.baseDatabaseVersion, this.numberOfItems); this.baseDatabaseVersion, this.numberOfItems);
loadInfoDataAfterLoadedHook(useAssets); loadInfoDataAfterLoadedHook(useInternal);
return true; return true;
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
@@ -142,13 +150,13 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
return false; return false;
} }
protected void loadInfoDataAfterLoadedHook(boolean useAssets) {} protected void loadInfoDataAfterLoadedHook(boolean useInternal) {}
protected boolean loadSliceListData(boolean useAssets) { protected boolean loadSliceListData(boolean useInternal) {
LOG.debug("loadSliceListData() started"); LOG.debug("loadSliceListData() started");
String fileName = getPathPrefix() + getNamePrefix() + "list.dat"; String fileName = getPathPrefix() + getNamePrefix() + "list.dat";
try (InputStream is = openFile(fileName, useAssets); try (InputStream is = storage.openFile(fileName, useInternal);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) {
String nodes = bufferedReader.readLine(); String nodes = bufferedReader.readLine();
@@ -221,13 +229,13 @@ public abstract class AbstractDatabase<T extends AbstractDatabaseDataSlice<V>, V
LOG.debug("loadSlice() started with sliceId={}", sliceId); LOG.debug("loadSlice() started with sliceId={}", sliceId);
String fileName = getPathPrefix() + getNamePrefix() + sliceId + ".dat"; String fileName = getPathPrefix() + getNamePrefix() + sliceId + ".dat";
loadSlice(slice, fileName, useAssets); loadSlice(slice, fileName, useInternal);
} }
protected void loadSlice(T slice, String fileName, boolean useAssets) { protected void loadSlice(T slice, String fileName, boolean useInternal) {
LOG.debug("loadSlice() started with fileName={}, useAssets={}", fileName, useAssets); LOG.debug("loadSlice() started with fileName={}, useInternal={}", fileName, useInternal);
try (InputStream is = openFile(fileName, useAssets); try (InputStream is = storage.openFile(fileName, useInternal);
BufferedInputStream stream = new BufferedInputStream(is)) { BufferedInputStream stream = new BufferedInputStream(is)) {
slice.loadFromStream(stream); slice.loadFromStream(stream);

View File

@@ -1,9 +1,10 @@
package dummydomain.yetanothercallblocker.sia.model.database; package dummydomain.yetanothercallblocker.sia.model.database;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import androidx.annotation.Nullable;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -22,17 +23,14 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import dummydomain.yetanothercallblocker.sia.Settings;
import dummydomain.yetanothercallblocker.sia.Storage;
import dummydomain.yetanothercallblocker.sia.network.WebService; import dummydomain.yetanothercallblocker.sia.network.WebService;
import dummydomain.yetanothercallblocker.sia.utils.FileUtils; import dummydomain.yetanothercallblocker.sia.utils.FileUtils;
import dummydomain.yetanothercallblocker.sia.utils.Utils;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import static dummydomain.yetanothercallblocker.sia.utils.FileUtils.getDataDir;
import static dummydomain.yetanothercallblocker.sia.utils.FileUtils.getDataDirPath;
import static dummydomain.yetanothercallblocker.sia.utils.FileUtils.openFile;
public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSlice, CommunityDatabaseItem> { public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSlice, CommunityDatabaseItem> {
private enum UpdateResult { private enum UpdateResult {
@@ -45,22 +43,28 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
private int siaAppVersion = FALLBACK_APP_VERSION; private int siaAppVersion = FALLBACK_APP_VERSION;
protected final String secondaryPathPrefix; private final String secondaryPathPrefix;
private final Settings settings;
private final WebService webService;
private SparseArray<CommunityDatabaseDataSlice> secondarySliceCache = new SparseArray<>(); private SparseArray<CommunityDatabaseDataSlice> secondarySliceCache = new SparseArray<>();
@SuppressLint("UseSparseArrays") // uses null as a special value @SuppressLint("UseSparseArrays") // uses null as a special value
private SparseArray<Boolean> existingSecondarySliceFiles = new SparseArray<>(); private SparseArray<Boolean> existingSecondarySliceFiles = new SparseArray<>();
public CommunityDatabase(String pathPrefix, String secondaryPathPrefix) { public CommunityDatabase(Storage storage, Source source,
super(pathPrefix); String pathPrefix, String secondaryPathPrefix,
Settings settings, WebService webService) {
super(storage, source, pathPrefix);
this.secondaryPathPrefix = secondaryPathPrefix; this.secondaryPathPrefix = secondaryPathPrefix;
this.settings = settings;
this.webService = webService;
} }
public int getEffectiveDbVersion() { public int getEffectiveDbVersion() {
checkLoaded(); checkLoaded();
int secondaryDbVersion = Utils.getSettings().getSecondaryDbVersion(); int secondaryDbVersion = settings.getSecondaryDbVersion();
return secondaryDbVersion > 0 ? secondaryDbVersion : baseDatabaseVersion; return secondaryDbVersion > 0 ? secondaryDbVersion : baseDatabaseVersion;
} }
@@ -83,13 +87,13 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
} }
@Override @Override
protected boolean load(boolean useAssets) { protected boolean load(boolean useInternal) {
if (!super.load(useAssets)) return false; if (!super.load(useInternal)) return false;
LOG.debug("load() started; useAssets={}", useAssets); LOG.debug("load() started; useInternal={}", useInternal);
String fileName = getPathPrefix() + "sia_info.dat"; String fileName = getPathPrefix() + "sia_info.dat";
try (InputStream is = openFile(fileName, useAssets); try (InputStream is = storage.openFile(fileName, useInternal);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) {
if (!"SIA".equals(bufferedReader.readLine())) { if (!"SIA".equals(bufferedReader.readLine())) {
@@ -109,13 +113,13 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
} }
@Override @Override
protected void loadInfoDataAfterLoadedHook(boolean useAssets) { protected void loadInfoDataAfterLoadedHook(boolean useInternal) {
int oldDbVersion = Utils.getSettings().getBaseDbVersion(); int oldDbVersion = settings.getBaseDbVersion();
if (baseDatabaseVersion != oldDbVersion) { if (baseDatabaseVersion != oldDbVersion) {
LOG.info("loadInfoDataAfterLoadedHook() base version changed; resetting secondary DB;" + LOG.info("loadInfoDataAfterLoadedHook() base version changed; resetting secondary DB;" +
" oldDbVersion={}, baseDatabaseVersion={}", oldDbVersion, baseDatabaseVersion); " oldDbVersion={}, baseDatabaseVersion={}", oldDbVersion, baseDatabaseVersion);
resetSecondaryDatabase(); resetSecondaryDatabase();
Utils.getSettings().setBaseDbVersion(baseDatabaseVersion); settings.setBaseDbVersion(baseDatabaseVersion);
} }
} }
@@ -182,7 +186,7 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
String path = getSecondarySliceFilePath(id); String path = getSecondarySliceFilePath(id);
Boolean exists = existingSecondarySliceFiles.get(id, null); Boolean exists = existingSecondarySliceFiles.get(id, null);
if (exists == null) { if (exists == null) {
exists = new File(getDataDirPath() + path).exists(); exists = new File(storage.getDataDirPath() + path).exists();
existingSecondarySliceFiles.put(id, exists); existingSecondarySliceFiles.put(id, exists);
} }
return exists ? path : null; return exists ? path : null;
@@ -195,7 +199,7 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
public void resetSecondaryDatabase() { public void resetSecondaryDatabase() {
LOG.debug("resetSecondaryDatabase() started"); LOG.debug("resetSecondaryDatabase() started");
File dir = new File(getDataDir(), getSecondaryDbPathPrefix()); File dir = new File(storage.getDataDirPath(), getSecondaryDbPathPrefix());
if (dir.exists()) { if (dir.exists()) {
for (File file : dir.listFiles()) { for (File file : dir.listFiles()) {
if (!file.delete()) { if (!file.delete()) {
@@ -206,17 +210,17 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
secondarySliceCache.clear(); secondarySliceCache.clear();
existingSecondarySliceFiles.clear(); existingSecondarySliceFiles.clear();
Utils.getSettings().setSecondaryDbVersion(0); settings.setSecondaryDbVersion(0);
LOG.info("resetSecondaryDatabase() secondary DB was reset"); LOG.info("resetSecondaryDatabase() secondary DB was reset");
} }
protected String getSecondaryDbPathPrefix() { private String getSecondaryDbPathPrefix() {
return secondaryPathPrefix; return secondaryPathPrefix;
} }
protected void createSecondaryDbDirectory() { private void createSecondaryDbDirectory() {
FileUtils.createDirectory(getDataDir(), getSecondaryDbPathPrefix()); FileUtils.createDirectory(storage.getDataDirPath(), getSecondaryDbPathPrefix());
} }
public boolean updateSecondaryDb() { public boolean updateSecondaryDb() {
@@ -262,10 +266,10 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
LOG.debug("updateSecondaryDbInternal() effectiveDbVersion={}", effectiveDbVersion); LOG.debug("updateSecondaryDbInternal() effectiveDbVersion={}", effectiveDbVersion);
String dbVersionParam = "_dbVer=" + effectiveDbVersion; String dbVersionParam = "_dbVer=" + effectiveDbVersion;
String urlPath = WebService.getGetDatabaseUrlPart() + "/cached?" + dbVersionParam; String urlPath = webService.getGetDatabaseUrlPart() + "/cached?" + dbVersionParam;
try { try {
Response response = WebService.call(urlPath, new HashMap<>()); Response response = webService.call(urlPath, new HashMap<>());
if (response != null) { if (response != null) {
ResponseBody body = response.body(); ResponseBody body = response.body();
MediaType contentType = body.contentType(); MediaType contentType = body.contentType();
@@ -274,7 +278,8 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
if (contentType != null && "application".equals(contentType.type())) { if (contentType != null && "application".equals(contentType.type())) {
LOG.trace("updateSecondaryDbInternal() saving response data to file"); LOG.trace("updateSecondaryDbInternal() saving response data to file");
File tempFile = File.createTempFile("sia", "database", Utils.getContext().getCacheDir()); File tempFile = File.createTempFile("sia", "database",
new File(storage.getCacheDirPath()));
int totalRead = 0; int totalRead = 0;
try (InputStream in = body.byteStream(); try (InputStream in = body.byteStream();
@@ -371,7 +376,7 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
} }
try (BufferedOutputStream stream = new BufferedOutputStream( try (BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(getDataDirPath() + filePath + ".update", false))) { new FileOutputStream(storage.getDataDirPath() + filePath + ".update", false))) {
sliceFromExistingFile.writeMerged(newSlice, stream); sliceFromExistingFile.writeMerged(newSlice, stream);
} }
@@ -384,7 +389,7 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
LOG.debug("updateSecondaryWithSlice() update files created, renaming files"); LOG.debug("updateSecondaryWithSlice() update files created, renaming files");
for (int sliceId : updatedIndexes) { for (int sliceId : updatedIndexes) {
String filePath = getDataDirPath() + getSecondarySliceFilePath(sliceId); String filePath = storage.getDataDirPath() + getSecondarySliceFilePath(sliceId);
File updatedFile = new File(filePath + ".update"); File updatedFile = new File(filePath + ".update");
File oldFile = new File(filePath); File oldFile = new File(filePath);
@@ -396,7 +401,7 @@ public class CommunityDatabase extends AbstractDatabase<CommunityDatabaseDataSli
} }
} }
Utils.getSettings().setSecondaryDbVersion(dataSlice.getDbVersion()); settings.setSecondaryDbVersion(dataSlice.getDbVersion());
secondarySliceCache.clear(); secondarySliceCache.clear();
existingSecondarySliceFiles.clear(); existingSecondarySliceFiles.clear();

View File

@@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import dummydomain.yetanothercallblocker.sia.SiaConstants; import dummydomain.yetanothercallblocker.sia.Storage;
import dummydomain.yetanothercallblocker.sia.network.DbDownloader; import dummydomain.yetanothercallblocker.sia.network.DbDownloader;
import dummydomain.yetanothercallblocker.sia.utils.FileUtils; import dummydomain.yetanothercallblocker.sia.utils.FileUtils;
@@ -13,17 +13,26 @@ public class DbManager {
private static final Logger LOG = LoggerFactory.getLogger(DbManager.class); private static final Logger LOG = LoggerFactory.getLogger(DbManager.class);
public static boolean downloadMainDb() { private static final String DEFAULT_URL = "https://gitlab.com/xynngh/YetAnotherCallBlocker_data/raw/zip_v1/archives/sia.zip";
String url = "https://gitlab.com/xynngh/YetAnotherCallBlocker_data/raw/zip_v1/archives/sia.zip";
return downloadMainDb(url); private final Storage storage;
private final String pathPrefix;
public DbManager(Storage storage, String pathPrefix) {
this.storage = storage;
this.pathPrefix = pathPrefix;
} }
public static boolean downloadMainDb(String url) { public boolean downloadMainDb() {
return downloadMainDb(DEFAULT_URL);
}
public boolean downloadMainDb(String url) {
LOG.debug("downloadMainDb() started"); LOG.debug("downloadMainDb() started");
File dataDir = FileUtils.getDataDir(); File dataDir = new File(storage.getDataDirPath());
String siaDir = SiaConstants.SIA_PATH_PREFIX; String siaDir = pathPrefix;
String tmpUpdateDir = siaDir.substring(0, siaDir.indexOf('/')) + "-tmp/"; String tmpUpdateDir = siaDir.substring(0, siaDir.indexOf('/')) + "-tmp/";
String oldDir = siaDir.substring(0, siaDir.indexOf('/')) + "-old/"; String oldDir = siaDir.substring(0, siaDir.indexOf('/')) + "-old/";
@@ -31,7 +40,7 @@ public class DbManager {
FileUtils.createDirectory(dataDir, tmpUpdateDir); FileUtils.createDirectory(dataDir, tmpUpdateDir);
LOG.debug("downloadMainDb() prepared dirs"); LOG.debug("downloadMainDb() prepared dirs");
if (DbDownloader.download(url, FileUtils.getDataDirPath() + tmpUpdateDir)) { if (DbDownloader.download(url, storage.getDataDirPath() + tmpUpdateDir)) {
LOG.debug("downloadMainDb() downloaded and unpacked"); LOG.debug("downloadMainDb() downloaded and unpacked");
File old = new File(dataDir, siaDir); File old = new File(dataDir, siaDir);

View File

@@ -1,9 +1,11 @@
package dummydomain.yetanothercallblocker.sia.model.database; package dummydomain.yetanothercallblocker.sia.model.database;
import dummydomain.yetanothercallblocker.sia.Storage;
public class FeaturedDatabase extends AbstractDatabase<FeaturedDatabaseDataSlice, FeaturedDatabaseItem> { public class FeaturedDatabase extends AbstractDatabase<FeaturedDatabaseDataSlice, FeaturedDatabaseItem> {
public FeaturedDatabase(String pathPrefix) { public FeaturedDatabase(Storage storage, Source source, String pathPrefix) {
super(pathPrefix); super(storage, source, pathPrefix);
} }
@Override @Override

View File

@@ -1,7 +1,5 @@
package dummydomain.yetanothercallblocker.sia.network; package dummydomain.yetanothercallblocker.sia.network;
import android.os.Build;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -20,6 +18,52 @@ import okhttp3.ResponseBody;
public class WebService { public class WebService {
public interface WSParameterProvider {
String getAppId();
String getDevice();
String getModel();
String getManufacturer();
int getAndroidApi();
String getAppFamily();
int getAppVersion();
int getDbVersion();
String getCountry();
}
public static abstract class DefaultWSParameterProvider implements WSParameterProvider {
@Override
public String getDevice() {
return "dreamlte";
}
@Override
public String getModel() {
return "SM-G950F";
}
@Override
public String getManufacturer() {
return "Samsung";
}
@Override
public int getAndroidApi() {
return 26;
}
@Override
public String getAppFamily() {
return "SIA-NEXT";
}
@Override
public String getCountry() {
return "US";
}
}
public static class WSResponse { public static class WSResponse {
private boolean successful; private boolean successful;
private int responseCode; private int responseCode;
@@ -60,15 +104,21 @@ public class WebService {
private static final String GET_DATABASE_URL_PART = "/get-database"; private static final String GET_DATABASE_URL_PART = "/get-database";
private static final String GET_REVIEWS_URL_PART = "/get-reviews"; private static final String GET_REVIEWS_URL_PART = "/get-reviews";
public static String getGetDatabaseUrlPart() { private final WSParameterProvider parameterProvider;
public WebService(WSParameterProvider parameterProvider) {
this.parameterProvider = parameterProvider;
}
public String getGetDatabaseUrlPart() {
return GET_DATABASE_URL_PART; return GET_DATABASE_URL_PART;
} }
public static String getGetReviewsUrlPart() { public String getGetReviewsUrlPart() {
return GET_REVIEWS_URL_PART; return GET_REVIEWS_URL_PART;
} }
public static Response call(String path, Map<String, String> params) { public Response call(String path, Map<String, String> params) {
LOG.debug("call() started; path={}", path); LOG.debug("call() started; path={}", path);
// TODO: retries // TODO: retries
@@ -82,7 +132,7 @@ public class WebService {
return null; return null;
} }
public static WSResponse callForJson(String path, Map<String, String> params) { public WSResponse callForJson(String path, Map<String, String> params) {
LOG.debug("callForJson() started; path={}", path); LOG.debug("callForJson() started; path={}", path);
try { try {
@@ -118,24 +168,24 @@ public class WebService {
return null; return null;
} }
private static String addHostname(String path) { private String addHostname(String path) {
return "https://aapi.shouldianswer.net/srvapp" + path; return "https://aapi.shouldianswer.net/srvapp" + path;
} }
private static RequestBody createRequestBody(Map<String, String> params) { private RequestBody createRequestBody(Map<String, String> params) {
if (params == null) params = new HashMap<>(); if (params == null) params = new HashMap<>();
params.put("_appId", Utils.getAppId()); params.put("_appId", parameterProvider.getAppId());
params.put("_device", "dreamlte"); params.put("_device", parameterProvider.getDevice());
params.put("_model", "SM-G950F"); params.put("_model", parameterProvider.getModel());
params.put("_manufacturer", "Samsung"); params.put("_manufacturer", parameterProvider.getManufacturer());
params.put("_api", String.valueOf(Build.VERSION_CODES.O)); params.put("_api", String.valueOf(parameterProvider.getAndroidApi()));
params.put("_appFamily", "SIA-NEXT"); params.put("_appFamily", parameterProvider.getAppFamily());
params.put("_appVer", String.valueOf(Utils.getAppVersion())); params.put("_appVer", String.valueOf(parameterProvider.getAppVersion()));
params.put("_dbVer", String.valueOf(Utils.getEffectiveDbVersion())); params.put("_dbVer", String.valueOf(parameterProvider.getDbVersion()));
params.put("_country", "US"); // TODO: remove hardcode params.put("_country", parameterProvider.getCountry());
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

View File

@@ -4,34 +4,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileUtils { public class FileUtils {
private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class); private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);
public static File getDataDir() {
return Utils.getContext().getFilesDir();
}
public static String getDataDirPath() {
return getDataDir().getAbsolutePath() + "/";
}
public static InputStream openFile(String fileName, boolean asset) throws IOException {
if (asset) {
return Utils.getContext().getAssets().open(fileName);
} else {
return new FileInputStream(getDataDirPath() + fileName);
}
}
public static File createDirectory(String path) { public static File createDirectory(String path) {
return createDirectory(new File(path)); return createDirectory(new File(path));
} }
public static File createDirectory(String base, String dir) {
return createDirectory(new File(base, dir));
}
public static File createDirectory(File base, String dir) { public static File createDirectory(File base, String dir) {
return createDirectory(new File(base, dir)); return createDirectory(new File(base, dir));
} }

View File

@@ -1,6 +1,5 @@
package dummydomain.yetanothercallblocker.sia.utils; package dummydomain.yetanothercallblocker.sia.utils;
import android.content.Context;
import android.util.Base64; import android.util.Base64;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
@@ -9,36 +8,12 @@ import org.apache.commons.codec.digest.DigestUtils;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.UUID; import java.util.UUID;
import dummydomain.yetanothercallblocker.App;
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.SiaSettings;
public class Utils { public class Utils {
public static Context getContext() {
return App.getInstance();
}
public static SiaSettings getSettings() {
return new SiaSettings(getContext());
}
public static String md5String(String str) { public static String md5String(String str) {
return new String(Hex.encodeHex(DigestUtils.md5(str))); return new String(Hex.encodeHex(DigestUtils.md5(str)));
} }
public static int getAppVersion() {
return DatabaseSingleton.getCommunityDatabase().getSiaAppVersion();
}
public static int getEffectiveDbVersion() {
return DatabaseSingleton.getCommunityDatabase().getEffectiveDbVersion();
}
public static String getAppId() {
return "qQq0O9nCRNy_aVdPgU9WOA";
}
public static String generateAppId() { public static String generateAppId() {
UUID randomUUID = UUID.randomUUID(); UUID randomUUID = UUID.randomUUID();

View File

@@ -22,7 +22,6 @@ import dummydomain.yetanothercallblocker.event.MainDbDownloadFinishedEvent;
import dummydomain.yetanothercallblocker.event.MainDbDownloadingEvent; import dummydomain.yetanothercallblocker.event.MainDbDownloadingEvent;
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished; import dummydomain.yetanothercallblocker.event.SecondaryDbUpdateFinished;
import dummydomain.yetanothercallblocker.event.SecondaryDbUpdatingEvent; import dummydomain.yetanothercallblocker.event.SecondaryDbUpdatingEvent;
import dummydomain.yetanothercallblocker.sia.model.database.DbManager;
import static dummydomain.yetanothercallblocker.EventUtils.postEvent; import static dummydomain.yetanothercallblocker.EventUtils.postEvent;
import static dummydomain.yetanothercallblocker.EventUtils.postStickyEvent; import static dummydomain.yetanothercallblocker.EventUtils.postStickyEvent;
@@ -89,7 +88,7 @@ public class TaskService extends IntentService {
postStickyEvent(sticky); postStickyEvent(sticky);
try { try {
DbManager.downloadMainDb(); DatabaseSingleton.getDbManager().downloadMainDb();
DatabaseSingleton.getCommunityDatabase().reload(); DatabaseSingleton.getCommunityDatabase().reload();
DatabaseSingleton.getFeaturedDatabase().reload(); DatabaseSingleton.getFeaturedDatabase().reload();
} finally { } finally {