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 dummydomain.yetanothercallblocker.data.ContactsHelper;
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.data.Config;
public class App extends Application {
@ -31,8 +30,7 @@ public class App extends Application {
NotificationHelper.createNotificationChannels(this);
DatabaseSingleton.setContactsProvider(
ContactsHelper.getContactsProvider(this, settings));
Config.init(this, settings);
}
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 dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.model.CommunityReview;
import dummydomain.yetanothercallblocker.sia.model.CommunityReviewsLoader;
public class ReviewsActivity extends AppCompatActivity {
@ -59,7 +59,7 @@ public class ReviewsActivity extends AppCompatActivity {
= new AsyncTask<Void, Void, List<CommunityReview>>() {
@Override
protected List<CommunityReview> doInBackground(Void... voids) {
return CommunityReviewsLoader.loadReviews(paramNumber);
return DatabaseSingleton.getCommunityReviewsLoader().loadReviews(paramNumber);
}
@Override

View File

@ -1,9 +1,8 @@
package dummydomain.yetanothercallblocker;
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_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_CHECK_TIME = "lastUpdateCheckTime";
private final SharedPreferences pref;
Settings(Context context) {
pref = context.getSharedPreferences("yacb_preferences", Context.MODE_PRIVATE);
super(context, "yacb_preferences");
}
public boolean getIncomingCallNotifications() {
@ -57,24 +54,4 @@ public class Settings {
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.net.Uri;
import android.provider.ContactsContract;
import android.text.TextUtils;
import dummydomain.yetanothercallblocker.PermissionHelper;
import dummydomain.yetanothercallblocker.Settings;
public class ContactsHelper {
@ -16,16 +12,6 @@ public class ContactsHelper {
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) {
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(number));

View File

@ -5,33 +5,61 @@ import android.text.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dummydomain.yetanothercallblocker.sia.model.CommunityReviewsLoader;
import dummydomain.yetanothercallblocker.sia.model.database.CommunityDatabase;
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.FeaturedDatabaseItem;
import static dummydomain.yetanothercallblocker.sia.SiaConstants.SIA_PATH_PREFIX;
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);
public class DatabaseSingleton {
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;
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() {
return COMMUNITY_DATABASE;
return communityDatabase;
}
public static FeaturedDatabase getFeaturedDatabase() {
return FEATURED_DATABASE;
return featuredDatabase;
}
public static void setContactsProvider(ContactsProvider contactsProvider) {
DatabaseSingleton.contactsProvider = contactsProvider;
public static CommunityReviewsLoader getCommunityReviewsLoader() {
return communityReviewsLoader;
}
public static NumberInfo getNumberInfo(String number) {
@ -46,7 +74,7 @@ public class DatabaseSingleton { // TODO: rewrite
.getDbItemByNumber(number);
LOG.trace("getNumberInfo() featuredItem={}", featuredItem);
ContactItem contactItem = contactsProvider != null ? contactsProvider.get(number) : null;
ContactItem contactItem = DatabaseSingleton.contactsProvider.get(number);
LOG.trace("getNumberInfo() contactItem={}", contactItem);
NumberInfo numberInfo = new NumberInfo();

View File

@ -1,7 +1,8 @@
package dummydomain.yetanothercallblocker.sia;
package dummydomain.yetanothercallblocker.data;
public interface SiaConstants {
String SIA_PROPERTIES = "sia_preferences";
String SIA_PATH_PREFIX = "sia/";
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);
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);
if (number.startsWith("+")) {
@ -26,9 +34,9 @@ public class CommunityReviewsLoader {
Map<String, String> params = new HashMap<>();
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<>();

View File

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

View File

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

View File

@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import dummydomain.yetanothercallblocker.sia.SiaConstants;
import dummydomain.yetanothercallblocker.sia.Storage;
import dummydomain.yetanothercallblocker.sia.network.DbDownloader;
import dummydomain.yetanothercallblocker.sia.utils.FileUtils;
@ -13,17 +13,26 @@ public class DbManager {
private static final Logger LOG = LoggerFactory.getLogger(DbManager.class);
public static boolean downloadMainDb() {
String url = "https://gitlab.com/xynngh/YetAnotherCallBlocker_data/raw/zip_v1/archives/sia.zip";
return downloadMainDb(url);
private static final String DEFAULT_URL = "https://gitlab.com/xynngh/YetAnotherCallBlocker_data/raw/zip_v1/archives/sia.zip";
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");
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 oldDir = siaDir.substring(0, siaDir.indexOf('/')) + "-old/";
@ -31,7 +40,7 @@ public class DbManager {
FileUtils.createDirectory(dataDir, tmpUpdateDir);
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");
File old = new File(dataDir, siaDir);

View File

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

View File

@ -1,7 +1,5 @@
package dummydomain.yetanothercallblocker.sia.network;
import android.os.Build;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,6 +18,52 @@ import okhttp3.ResponseBody;
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 {
private boolean successful;
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_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;
}
public static String getGetReviewsUrlPart() {
public String getGetReviewsUrlPart() {
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);
// TODO: retries
@ -82,7 +132,7 @@ public class WebService {
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);
try {
@ -118,24 +168,24 @@ public class WebService {
return null;
}
private static String addHostname(String path) {
private String addHostname(String 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<>();
params.put("_appId", Utils.getAppId());
params.put("_appId", parameterProvider.getAppId());
params.put("_device", "dreamlte");
params.put("_model", "SM-G950F");
params.put("_manufacturer", "Samsung");
params.put("_api", String.valueOf(Build.VERSION_CODES.O));
params.put("_device", parameterProvider.getDevice());
params.put("_model", parameterProvider.getModel());
params.put("_manufacturer", parameterProvider.getManufacturer());
params.put("_api", String.valueOf(parameterProvider.getAndroidApi()));
params.put("_appFamily", "SIA-NEXT");
params.put("_appVer", String.valueOf(Utils.getAppVersion()));
params.put("_dbVer", String.valueOf(Utils.getEffectiveDbVersion()));
params.put("_country", "US"); // TODO: remove hardcode
params.put("_appFamily", parameterProvider.getAppFamily());
params.put("_appVer", String.valueOf(parameterProvider.getAppVersion()));
params.put("_dbVer", String.valueOf(parameterProvider.getDbVersion()));
params.put("_country", parameterProvider.getCountry());
MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

View File

@ -4,34 +4,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileUtils {
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) {
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) {
return createDirectory(new File(base, dir));
}

View File

@ -1,6 +1,5 @@
package dummydomain.yetanothercallblocker.sia.utils;
import android.content.Context;
import android.util.Base64;
import org.apache.commons.codec.binary.Hex;
@ -9,36 +8,12 @@ import org.apache.commons.codec.digest.DigestUtils;
import java.nio.ByteBuffer;
import java.util.UUID;
import dummydomain.yetanothercallblocker.App;
import dummydomain.yetanothercallblocker.data.DatabaseSingleton;
import dummydomain.yetanothercallblocker.sia.SiaSettings;
public class Utils {
public static Context getContext() {
return App.getInstance();
}
public static SiaSettings getSettings() {
return new SiaSettings(getContext());
}
public static String md5String(String 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() {
UUID randomUUID = UUID.randomUUID();

View File

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