diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index a1fade17c..18fc3d916 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -27,6 +27,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; @@ -267,6 +268,7 @@ public class FDroidApp extends Application { public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Languages.setLanguage(this); + UpdateService.forceUpdateRepo(this); } @Override @@ -386,12 +388,12 @@ public class FDroidApp extends Application { ImageLoader.getInstance().init(config); FDroidApp.initWifiSettings(); - startService(new Intent(this, WifiStateChangeService.class)); + WifiStateChangeService.start(this, null); // if the HTTPS pref changes, then update all affected things Preferences.get().registerLocalRepoHttpsListeners(new ChangeListener() { @Override public void onPreferenceChange() { - startService(new Intent(FDroidApp.this, WifiStateChangeService.class)); + WifiStateChangeService.start(getApplicationContext(), null); } }); @@ -414,6 +416,13 @@ public class FDroidApp extends Application { // find and process provisions if any. Provisioner.scanAndProcess(getApplicationContext()); + + // if the underlying OS version has changed, then fully rebuild the database + SharedPreferences atStartTime = getSharedPreferences("at-start-time", Context.MODE_PRIVATE); + if (Build.VERSION.SDK_INT != atStartTime.getInt("build-version", Build.VERSION.SDK_INT)) { + UpdateService.forceUpdateRepo(this); + } + atStartTime.edit().putInt("build-version", Build.VERSION.SDK_INT).apply(); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/Preferences.java b/app/src/main/java/org/fdroid/fdroid/Preferences.java index 53b604cb6..e7bd2dfa3 100644 --- a/app/src/main/java/org/fdroid/fdroid/Preferences.java +++ b/app/src/main/java/org/fdroid/fdroid/Preferences.java @@ -2,7 +2,6 @@ package org.fdroid.fdroid; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; @@ -39,10 +38,6 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh .putString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName()) .apply(); } - Resources res = context.getResources(); - defaultPreventScreenshots = res.getBoolean(R.bool.defaultPreventScreenshots); - defaultPanicExit = res.getBoolean(R.bool.defaultPanicExit); - defaultHideOnLongPressSearch = res.getBoolean(R.bool.defaultHideOnLongPressSearch); } public static final String PREF_UPD_INTERVAL = "updateInterval"; @@ -94,9 +89,9 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh private static final boolean DEFAULT_SHOW_NFC_DURING_SWAP = true; private static final boolean DEFAULT_FORCE_OLD_INDEX = false; private static final boolean DEFAULT_POST_PRIVILEGED_INSTALL = false; - private final boolean defaultPreventScreenshots; - private final boolean defaultPanicExit; - private final boolean defaultHideOnLongPressSearch; + private static final boolean DEFAULT_PREVENT_SCREENSHOTS = false; + private static final boolean DEFAULT_PANIC_EXIT = true; + private static final boolean DEFAULT_HIDE_ON_LONG_PRESS_SEARCH = false; public enum Theme { light, @@ -327,11 +322,11 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh } public boolean preventScreenshots() { - return preferences.getBoolean(PREF_PREVENT_SCREENSHOTS, defaultPreventScreenshots); + return preferences.getBoolean(PREF_PREVENT_SCREENSHOTS, DEFAULT_PREVENT_SCREENSHOTS); } public boolean panicExit() { - return preferences.getBoolean(PREF_PANIC_EXIT, defaultPanicExit); + return preferences.getBoolean(PREF_PANIC_EXIT, DEFAULT_PANIC_EXIT); } public boolean panicHide() { @@ -339,7 +334,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh } public boolean hideOnLongPressSearch() { - return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, defaultHideOnLongPressSearch); + return preferences.getBoolean(PREF_HIDE_ON_LONG_PRESS_SEARCH, DEFAULT_HIDE_ON_LONG_PRESS_SEARCH); } /** @@ -458,8 +453,9 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh * While it is likely that most places asking for preferences have access to a {@link Context}, * it is a minor convenience to be able to ask for preferences without. */ - public static void clearSingletonForTesting() { + public static void setupForTests(Context context) { instance = null; + setup(context); } /** diff --git a/app/src/main/java/org/fdroid/fdroid/UpdateService.java b/app/src/main/java/org/fdroid/fdroid/UpdateService.java index e6a02303f..1b9a5a62b 100644 --- a/app/src/main/java/org/fdroid/fdroid/UpdateService.java +++ b/app/src/main/java/org/fdroid/fdroid/UpdateService.java @@ -40,11 +40,11 @@ import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; - import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.AppProvider; +import org.fdroid.fdroid.data.DBHelper; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.Schema; @@ -66,6 +66,7 @@ public class UpdateService extends IntentService { public static final String EXTRA_STATUS_CODE = "status"; public static final String EXTRA_ADDRESS = "address"; public static final String EXTRA_MANUAL_UPDATE = "manualUpdate"; + public static final String EXTRA_FORCED_UPDATE = "forcedUpdate"; public static final String EXTRA_PROGRESS = "progress"; public static final int STATUS_COMPLETE_WITH_CHANGES = 0; @@ -96,10 +97,10 @@ public class UpdateService extends IntentService { } public static void updateNow(Context context) { - updateRepoNow(null, context); + updateRepoNow(context, null); } - public static void updateRepoNow(String address, Context context) { + public static void updateRepoNow(Context context, String address) { Intent intent = new Intent(context, UpdateService.class); intent.putExtra(EXTRA_MANUAL_UPDATE, true); if (!TextUtils.isEmpty(address)) { @@ -108,6 +109,17 @@ public class UpdateService extends IntentService { context.startService(intent); } + /** + * For when an automatic process needs to force an index update, like + * when the system language changes, or the underlying OS was upgraded. + * This wipes the existing database before running the update! + */ + public static void forceUpdateRepo(Context context) { + Intent intent = new Intent(context, UpdateService.class); + intent.putExtra(EXTRA_FORCED_UPDATE, true); + context.startService(intent); + } + /** * Schedule or cancel this service to update the app index, according to the * current preferences. Should be called a) at boot, b) if the preference @@ -225,7 +237,7 @@ public class UpdateService extends IntentService { switch (resultCode) { case STATUS_INFO: notificationBuilder.setContentText(message) - .setCategory(NotificationCompat.CATEGORY_SERVICE); + .setCategory(NotificationCompat.CATEGORY_SERVICE); if (progress != -1) { notificationBuilder.setProgress(100, progress, false); } else { @@ -236,8 +248,8 @@ public class UpdateService extends IntentService { case STATUS_ERROR_GLOBAL: text = context.getString(R.string.global_error_updating_repos, message); notificationBuilder.setContentText(text) - .setCategory(NotificationCompat.CATEGORY_ERROR) - .setSmallIcon(android.R.drawable.ic_dialog_alert); + .setCategory(NotificationCompat.CATEGORY_ERROR) + .setSmallIcon(android.R.drawable.ic_dialog_alert); notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build()); Toast.makeText(context, text, Toast.LENGTH_LONG).show(); break; @@ -254,8 +266,8 @@ public class UpdateService extends IntentService { } text = msgBuilder.toString(); notificationBuilder.setContentText(text) - .setCategory(NotificationCompat.CATEGORY_ERROR) - .setSmallIcon(android.R.drawable.ic_dialog_info); + .setCategory(NotificationCompat.CATEGORY_ERROR) + .setSmallIcon(android.R.drawable.ic_dialog_info); notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build()); Toast.makeText(context, text, Toast.LENGTH_LONG).show(); break; @@ -264,7 +276,7 @@ public class UpdateService extends IntentService { case STATUS_COMPLETE_AND_SAME: text = context.getString(R.string.repos_unchanged); notificationBuilder.setContentText(text) - .setCategory(NotificationCompat.CATEGORY_SERVICE); + .setCategory(NotificationCompat.CATEGORY_SERVICE); notificationManager.notify(NOTIFY_ID_UPDATING, notificationBuilder.build()); break; } @@ -349,10 +361,12 @@ public class UpdateService extends IntentService { final long startTime = System.currentTimeMillis(); boolean manualUpdate = false; + boolean forcedUpdate = false; String address = null; if (intent != null) { address = intent.getStringExtra(EXTRA_ADDRESS); manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false); + forcedUpdate = intent.getBooleanExtra(EXTRA_FORCED_UPDATE, false); } try { @@ -366,8 +380,8 @@ public class UpdateService extends IntentService { return; } - if (manualUpdate) { - Utils.debugLog(TAG, "manually requested update"); + if (manualUpdate || forcedUpdate) { + Utils.debugLog(TAG, "manually requested or forced update"); } else if (!verifyIsTimeForScheduledRun() || (netState == FLAG_NET_METERED && Preferences.get().isUpdateOnlyOnUnmeteredNetworks())) { Utils.debugLog(TAG, "don't run update"); @@ -404,6 +418,9 @@ public class UpdateService extends IntentService { sendStatus(this, STATUS_INFO, getString(R.string.status_connecting_to_repo, repo.address)); + if (forcedUpdate) { + DBHelper.resetTransient(this); + } try { RepoUpdater updater = new IndexV1Updater(this, repo); diff --git a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java index 22555916b..d4c671167 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java +++ b/app/src/main/java/org/fdroid/fdroid/data/DBHelper.java @@ -28,32 +28,38 @@ import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import android.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; - +import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Schema.AntiFeatureTable; import org.fdroid.fdroid.data.Schema.ApkAntiFeatureJoinTable; import org.fdroid.fdroid.data.Schema.ApkTable; -import org.fdroid.fdroid.data.Schema.CatJoinTable; -import org.fdroid.fdroid.data.Schema.PackageTable; -import org.fdroid.fdroid.data.Schema.AppPrefsTable; import org.fdroid.fdroid.data.Schema.AppMetadataTable; +import org.fdroid.fdroid.data.Schema.AppPrefsTable; +import org.fdroid.fdroid.data.Schema.CatJoinTable; import org.fdroid.fdroid.data.Schema.InstalledAppTable; +import org.fdroid.fdroid.data.Schema.PackageTable; import org.fdroid.fdroid.data.Schema.RepoTable; import java.util.ArrayList; import java.util.List; +/** + * This is basically a singleton used to represent the database at the core + * of all of the {@link android.content.ContentProvider}s used at the core + * of this app. {@link DBHelper} is not {@code private} so that it can be easily + * used in test subclasses. + */ @SuppressWarnings("LineLength") -class DBHelper extends SQLiteOpenHelper { +public class DBHelper extends SQLiteOpenHelper { private static final String TAG = "DBHelper"; public static final int REPO_XML_ARG_COUNT = 8; + private static DBHelper instance; private static final String DATABASE_NAME = "fdroid"; private static final String CREATE_TABLE_PACKAGE = "CREATE TABLE " + PackageTable.NAME @@ -214,7 +220,22 @@ class DBHelper extends SQLiteOpenHelper { DBHelper(Context context) { super(context, DATABASE_NAME, null, DB_VERSION); - this.context = context; + this.context = context.getApplicationContext(); + } + + /** + * Only used for testing. Not quite sure how to mock a singleton variable like this. + */ + public static void clearDbHelperSingleton() { + instance = null; + } + + static synchronized DBHelper getInstance(Context context) { + if (instance == null) { + Utils.debugLog(TAG, "First time accessing database, creating new helper"); + instance = new DBHelper(context); + } + return instance; } @Override @@ -1070,13 +1091,19 @@ class DBHelper extends SQLiteOpenHelper { db.execSQL("update " + RepoTable.NAME + " set " + RepoTable.Cols.LAST_ETAG + " = NULL"); } - private void resetTransient(SQLiteDatabase db) { - Utils.debugLog(TAG, "Removing app + apk tables so they can be recreated. Next time F-Droid updates it should trigger an index update."); + /** + * Resets all database tables that are generated from the index files downloaded + * from the active repositories. This will trigger the index file(s) to be + * downloaded processed on the next update. + */ + public static void resetTransient(Context context) { + resetTransient(getInstance(context).getWritableDatabase()); + } - PreferenceManager.getDefaultSharedPreferences(context) - .edit() - .putBoolean("triedEmptyUpdate", false) - .apply(); + private static void resetTransient(SQLiteDatabase db) { + Utils.debugLog(TAG, "Removing all index tables, they will be recreated next time F-Droid updates."); + + Preferences.get().setTriedEmptyUpdate(false); db.beginTransaction(); try { @@ -1128,10 +1155,7 @@ class DBHelper extends SQLiteOpenHelper { return; } - PreferenceManager.getDefaultSharedPreferences(context) - .edit() - .putBoolean("triedEmptyUpdate", false) - .apply(); + Preferences.get().setTriedEmptyUpdate(false); db.execSQL("drop table " + AppMetadataTable.NAME); db.execSQL("drop table " + ApkTable.NAME); diff --git a/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java b/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java index da1bde5d1..4e561c407 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java +++ b/app/src/main/java/org/fdroid/fdroid/data/FDroidProvider.java @@ -5,16 +5,13 @@ import android.content.ContentProvider; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; -import android.content.Context; import android.content.OperationApplicationException; import android.content.UriMatcher; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.Build; import android.support.annotation.NonNull; - import org.fdroid.fdroid.BuildConfig; -import org.fdroid.fdroid.Utils; import java.util.ArrayList; import java.util.HashSet; @@ -23,15 +20,13 @@ import java.util.Set; public abstract class FDroidProvider extends ContentProvider { - private static final String TAG = "FDroidProvider"; + public static final String TAG = "FDroidProvider"; static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".data"; - static final int CODE_LIST = 1; + static final int CODE_LIST = 1; static final int CODE_SINGLE = 2; - private static DBHelper dbHelper; - private boolean isApplyingBatch; protected abstract String getTableName(); @@ -73,28 +68,13 @@ public abstract class FDroidProvider extends ContentProvider { return result; } - /** - * Only used for testing. Not quite sure how to mock a singleton variable like this. - */ - public static void clearDbHelperSingleton() { - dbHelper = null; - } - - private static synchronized DBHelper getOrCreateDb(Context context) { - if (dbHelper == null) { - Utils.debugLog(TAG, "First time accessing database, creating new helper"); - dbHelper = new DBHelper(context); - } - return dbHelper; - } - @Override public boolean onCreate() { return true; } protected final synchronized SQLiteDatabase db() { - return getOrCreateDb(getContext().getApplicationContext()).getWritableDatabase(); + return DBHelper.getInstance(getContext()).getWritableDatabase(); } @Override @@ -154,7 +134,7 @@ public abstract class FDroidProvider extends ContentProvider { if (!isValid) { throw new IllegalArgumentException( - "Cannot save field '" + key + "' to provider " + getProviderName()); + "Cannot save field '" + key + "' to provider " + getProviderName()); } } } diff --git a/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java b/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java index 5fb70b1bf..b3099cfb5 100644 --- a/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java +++ b/app/src/main/java/org/fdroid/fdroid/localrepo/SwapService.java @@ -205,7 +205,7 @@ public class SwapService extends Service { askServerToSwapWithUs(peerRepo); } - UpdateService.updateRepoNow(peer.getRepoAddress(), this); + UpdateService.updateRepoNow(this, peer.getRepoAddress()); } private void askServerToSwapWithUs(final Repo repo) { diff --git a/app/src/main/java/org/fdroid/fdroid/localrepo/type/WifiSwap.java b/app/src/main/java/org/fdroid/fdroid/localrepo/type/WifiSwap.java index 60cbece5f..152877aec 100644 --- a/app/src/main/java/org/fdroid/fdroid/localrepo/type/WifiSwap.java +++ b/app/src/main/java/org/fdroid/fdroid/localrepo/type/WifiSwap.java @@ -2,23 +2,16 @@ package org.fdroid.fdroid.localrepo.type; import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; - import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.localrepo.SwapService; import org.fdroid.fdroid.net.LocalHTTPD; import org.fdroid.fdroid.net.WifiStateChangeService; - -import java.io.IOException; -import java.net.BindException; -import java.util.Random; - import rx.Single; import rx.SingleSubscriber; import rx.android.schedulers.AndroidSchedulers; @@ -26,6 +19,10 @@ import rx.functions.Action1; import rx.functions.Func2; import rx.schedulers.Schedulers; +import java.io.IOException; +import java.net.BindException; +import java.util.Random; + @SuppressWarnings("LineLength") public class WifiSwap extends SwapType { @@ -143,7 +140,7 @@ public class WifiSwap extends SwapType { } catch (BindException e) { int prev = FDroidApp.port; FDroidApp.port = FDroidApp.port + new Random().nextInt(1111); - context.startService(new Intent(context, WifiStateChangeService.class)); + WifiStateChangeService.start(context, null); singleSubscriber.onError(new Exception("port " + prev + " occupied, trying on " + FDroidApp.port + "!")); } catch (IOException e) { Log.e(TAG, "Could not start local repo HTTP server", e); diff --git a/app/src/main/java/org/fdroid/fdroid/net/WifiStateChangeService.java b/app/src/main/java/org/fdroid/fdroid/net/WifiStateChangeService.java index 1b41743ff..fcd62ad6c 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/WifiStateChangeService.java +++ b/app/src/main/java/org/fdroid/fdroid/net/WifiStateChangeService.java @@ -1,16 +1,17 @@ package org.fdroid.fdroid.net; import android.app.IntentService; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.DhcpInfo; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.support.annotation.Nullable; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; - import org.apache.commons.net.util.SubnetUtils; import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.Preferences; @@ -34,7 +35,7 @@ import java.util.Locale; * which is how it can be triggered by code, or it came in from the system * via {@link org.fdroid.fdroid.receiver.WifiStateChangeReceiver}, in * which case an instance of {@link NetworkInfo} is included. - * + *
* The work is done in a {@link Thread} so that new incoming {@code Intents}
* are not blocked by processing. A new {@code Intent} immediately nullifies
* the current state because it means that something about the wifi has
@@ -54,6 +55,14 @@ public class WifiStateChangeService extends IntentService {
super("WifiStateChangeService");
}
+ public static void start(Context context, @Nullable Intent intent) {
+ if (intent == null) {
+ intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ }
+ intent.setComponent(new ComponentName(context, WifiStateChangeService.class));
+ context.startService(intent);
+ }
+
@Override
protected void onHandleIntent(Intent intent) {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
@@ -92,7 +101,7 @@ public class WifiStateChangeService extends IntentService {
WifiInfo wifiInfo = null;
int wifiState = wifiManager.getWifiState();
-
+ int retryCount = 0;
while (FDroidApp.ipAddressString == null) {
if (isInterrupted()) { // can be canceled by a change via WifiStateChangeReceiver
return;
@@ -107,7 +116,7 @@ public class WifiStateChangeService extends IntentService {
try {
FDroidApp.subnetInfo = new SubnetUtils(FDroidApp.ipAddressString, netmask).getInfo();
} catch (IllegalArgumentException e) {
- // catch this mystery error: "java.lang.IllegalArgumentException: Could not parse [null/24]"
+ // catch mystery: "java.lang.IllegalArgumentException: Could not parse [null/24]"
e.printStackTrace();
}
}
@@ -123,6 +132,11 @@ public class WifiStateChangeService extends IntentService {
setIpInfoFromNetworkInterface();
}
+ if (retryCount > 120) {
+ return;
+ }
+ retryCount++;
+
if (FDroidApp.ipAddressString == null) {
Thread.sleep(1000);
Utils.debugLog(TAG, "waiting for an IP address...");
diff --git a/app/src/main/java/org/fdroid/fdroid/receiver/WifiStateChangeReceiver.java b/app/src/main/java/org/fdroid/fdroid/receiver/WifiStateChangeReceiver.java
index 8521d1cad..2a8e11607 100644
--- a/app/src/main/java/org/fdroid/fdroid/receiver/WifiStateChangeReceiver.java
+++ b/app/src/main/java/org/fdroid/fdroid/receiver/WifiStateChangeReceiver.java
@@ -1,11 +1,9 @@
package org.fdroid.fdroid.receiver;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
-
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.net.WifiStateChangeService;
@@ -15,8 +13,7 @@ public class WifiStateChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
- intent.setComponent(new ComponentName(context, WifiStateChangeService.class));
- context.startService(intent);
+ WifiStateChangeService.start(context, intent);
} else {
Utils.debugLog(TAG, "received unsupported Intent: " + intent);
}
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 1c59a906b..95581fea5 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -14,11 +14,6 @@