Merge branch 'fix-only-on-wifi' into 'master'
fix "Only on WiFi" to apply to all downloads See merge request fdroid/fdroidclient!669
This commit is contained in:
commit
3c14d273b2
@ -270,7 +270,12 @@
|
||||
<service
|
||||
android:name=".DeleteCacheService"
|
||||
android:exported="false"/>
|
||||
<service android:name=".net.WifiStateChangeService"/>
|
||||
<service
|
||||
android:name=".net.WifiStateChangeService"
|
||||
android:exported="false"/>
|
||||
<service
|
||||
android:name=".net.ConnectivityMonitorService"
|
||||
android:exported="false"/>
|
||||
<service android:name=".localrepo.SwapService"/>
|
||||
<service
|
||||
android:name=".installer.InstallManagerService"
|
||||
|
@ -64,6 +64,7 @@ import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.installer.ApkFileProvider;
|
||||
import org.fdroid.fdroid.installer.InstallHistoryService;
|
||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||
import org.fdroid.fdroid.net.ImageLoaderForUIL;
|
||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||
import org.fdroid.fdroid.views.hiding.HidingManager;
|
||||
@ -110,6 +111,8 @@ public class FDroidApp extends Application {
|
||||
public static volatile String bssid;
|
||||
public static volatile Repo repo = new Repo();
|
||||
|
||||
public static volatile int networkState = ConnectivityMonitorService.FLAG_NET_UNAVAILABLE;
|
||||
|
||||
private static volatile String lastWorkingMirror = null;
|
||||
private static volatile int numTries = Integer.MAX_VALUE;
|
||||
private static volatile int timeout = 10000;
|
||||
@ -374,11 +377,10 @@ public class FDroidApp extends Application {
|
||||
}
|
||||
});
|
||||
|
||||
final Context context = this;
|
||||
Preferences.get().registerUnstableUpdatesChangeListener(new Preferences.ChangeListener() {
|
||||
@Override
|
||||
public void onPreferenceChange() {
|
||||
AppProvider.Helper.calcSuggestedApks(context);
|
||||
AppProvider.Helper.calcSuggestedApks(FDroidApp.this);
|
||||
}
|
||||
});
|
||||
|
||||
@ -434,6 +436,8 @@ public class FDroidApp extends Application {
|
||||
.build();
|
||||
ImageLoader.getInstance().init(config);
|
||||
|
||||
ConnectivityMonitorService.registerAndStart(this);
|
||||
|
||||
FDroidApp.initWifiSettings();
|
||||
WifiStateChangeService.start(this, null);
|
||||
// if the HTTPS pref changes, then update all affected things
|
||||
|
@ -6,6 +6,7 @@ import android.os.Build;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import info.guardianproject.netcipher.NetCipher;
|
||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
@ -273,8 +274,14 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
||||
return preferences.getBoolean(PREF_AUTO_DOWNLOAD_INSTALL_UPDATES, false);
|
||||
}
|
||||
|
||||
public boolean isUpdateOnlyOnUnmeteredNetworks() {
|
||||
return preferences.getBoolean(PREF_UPD_WIFI_ONLY, false);
|
||||
/**
|
||||
* Do the network conditions and user preferences allow for things to be
|
||||
* downloaded in the background.
|
||||
*/
|
||||
public boolean isBackgroundDownloadAllowed() {
|
||||
return FDroidApp.networkState == ConnectivityMonitorService.FLAG_NET_NO_LIMIT ||
|
||||
(FDroidApp.networkState == ConnectivityMonitorService.FLAG_NET_METERED
|
||||
&& !preferences.getBoolean(PREF_UPD_WIFI_ONLY, false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -27,8 +27,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -49,6 +47,7 @@ import org.fdroid.fdroid.data.Repo;
|
||||
import org.fdroid.fdroid.data.RepoProvider;
|
||||
import org.fdroid.fdroid.data.Schema;
|
||||
import org.fdroid.fdroid.installer.InstallManagerService;
|
||||
import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||
import org.fdroid.fdroid.views.main.MainActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -79,10 +78,6 @@ public class UpdateService extends IntentService {
|
||||
|
||||
private static final int NOTIFY_ID_UPDATING = 0;
|
||||
|
||||
private static final int FLAG_NET_UNAVAILABLE = 0;
|
||||
private static final int FLAG_NET_METERED = 1;
|
||||
private static final int FLAG_NET_NO_LIMIT = 2;
|
||||
|
||||
private static Handler toastHandler;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
@ -312,33 +307,6 @@ public class UpdateService extends IntentService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of internet availability, whether there is no connection at all,
|
||||
* whether the connection has no usage limit (like most WiFi), or whether this is
|
||||
* a metered connection like most cellular plans or hotspot WiFi connections.
|
||||
*/
|
||||
private static int getNetworkState(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
if (activeNetwork == null || !activeNetwork.isConnected()) {
|
||||
return FLAG_NET_UNAVAILABLE;
|
||||
}
|
||||
|
||||
int networkType = activeNetwork.getType();
|
||||
switch (networkType) {
|
||||
case ConnectivityManager.TYPE_ETHERNET:
|
||||
case ConnectivityManager.TYPE_WIFI:
|
||||
if (Build.VERSION.SDK_INT >= 16 && cm.isActiveNetworkMetered()) {
|
||||
return FLAG_NET_METERED;
|
||||
} else {
|
||||
return FLAG_NET_NO_LIMIT;
|
||||
}
|
||||
default:
|
||||
return FLAG_NET_METERED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In order to send a {@link Toast} from a {@link IntentService}, we have to do these tricks.
|
||||
*/
|
||||
@ -371,8 +339,8 @@ public class UpdateService extends IntentService {
|
||||
|
||||
try {
|
||||
// See if it's time to actually do anything yet...
|
||||
int netState = getNetworkState(this);
|
||||
if (netState == FLAG_NET_UNAVAILABLE) {
|
||||
int netState = ConnectivityMonitorService.getNetworkState(this);
|
||||
if (netState == ConnectivityMonitorService.FLAG_NET_UNAVAILABLE) {
|
||||
Utils.debugLog(TAG, "No internet, cannot update");
|
||||
if (manualUpdate) {
|
||||
sendNoInternetToast();
|
||||
@ -380,10 +348,10 @@ public class UpdateService extends IntentService {
|
||||
return;
|
||||
}
|
||||
|
||||
final Preferences fdroidPrefs = Preferences.get();
|
||||
if (manualUpdate || forcedUpdate) {
|
||||
Utils.debugLog(TAG, "manually requested or forced update");
|
||||
} else if (!verifyIsTimeForScheduledRun()
|
||||
|| (netState == FLAG_NET_METERED && Preferences.get().isUpdateOnlyOnUnmeteredNetworks())) {
|
||||
} else if (!verifyIsTimeForScheduledRun() || !fdroidPrefs.isBackgroundDownloadAllowed()) {
|
||||
Utils.debugLog(TAG, "don't run update");
|
||||
return;
|
||||
}
|
||||
@ -403,7 +371,6 @@ public class UpdateService extends IntentService {
|
||||
ArrayList<CharSequence> repoErrors = new ArrayList<>();
|
||||
boolean changes = false;
|
||||
boolean singleRepoUpdate = !TextUtils.isEmpty(address);
|
||||
final Preferences fdroidPrefs = Preferences.get();
|
||||
for (final Repo repo : repos) {
|
||||
if (!repo.inuse) {
|
||||
continue;
|
||||
@ -442,7 +409,7 @@ public class UpdateService extends IntentService {
|
||||
}
|
||||
|
||||
// now that downloading the index is done, start downloading updates
|
||||
if (changes && fdroidPrefs.isAutoDownloadEnabled()) {
|
||||
if (changes && fdroidPrefs.isAutoDownloadEnabled() && fdroidPrefs.isBackgroundDownloadAllowed()) {
|
||||
autoDownloadUpdates(this);
|
||||
}
|
||||
}
|
||||
|
@ -406,6 +406,12 @@ public final class Utils {
|
||||
return new Locale(languageTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since there have been vulnerabilities in EXIF processing in Android, this
|
||||
* disables all use of EXIF.
|
||||
*
|
||||
* @see <a href="https://securityaffairs.co/wordpress/51043/mobile-2/android-cve-2016-3862-flaw.html">CVE-2016-3862</a>
|
||||
*/
|
||||
public static DisplayImageOptions.Builder getDefaultDisplayImageOptionsBuilder() {
|
||||
if (defaultDisplayImageOptionsBuilder == null) {
|
||||
defaultDisplayImageOptionsBuilder = new DisplayImageOptions.Builder()
|
||||
|
@ -106,19 +106,23 @@ public abstract class Installer {
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this installation process has any new permissions that the user
|
||||
* should be aware of. Starting in {@code android-23}, all new permissions
|
||||
* are requested when they are used, and the permissions prompt at time of
|
||||
* install is not used. All permissions in a new install are considered new.
|
||||
*
|
||||
* @return the number of new permissions
|
||||
*/
|
||||
private int newPermissionCount() {
|
||||
boolean supportsRuntimePermissions = apk.targetSdkVersion >= 23;
|
||||
if (supportsRuntimePermissions) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
AppDiff appDiff = new AppDiff(context.getPackageManager(), apk);
|
||||
if (appDiff.pkgInfo == null) {
|
||||
// could not get diff because we couldn't parse the package
|
||||
throw new RuntimeException("cannot parse!");
|
||||
}
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo);
|
||||
if (appDiff.installedAppInfo != null) {
|
||||
AppDiff appDiff = new AppDiff(context, apk);
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo);
|
||||
if (appDiff.installedApplicationInfo != null) {
|
||||
// update to an existing app
|
||||
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
package org.fdroid.fdroid.net;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
|
||||
/**
|
||||
* An {@link IntentService} subclass for tracking whether there is metered or
|
||||
* unmetered internet available, based on
|
||||
* {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION}
|
||||
*/
|
||||
public class ConnectivityMonitorService extends IntentService {
|
||||
public static final String TAG = "ConnectivityMonitorServ";
|
||||
|
||||
public static final int FLAG_NET_UNAVAILABLE = 0;
|
||||
public static final int FLAG_NET_METERED = 1;
|
||||
public static final int FLAG_NET_NO_LIMIT = 2;
|
||||
|
||||
private static final String ACTION_START = "org.fdroid.fdroid.net.action.CONNECTIVITY_MONITOR";
|
||||
|
||||
private static final BroadcastReceiver CONNECTIVITY_RECEIVER = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
start(context);
|
||||
}
|
||||
};
|
||||
|
||||
public ConnectivityMonitorService() {
|
||||
super("ConnectivityMonitorService");
|
||||
}
|
||||
|
||||
public static void registerAndStart(Context context) {
|
||||
context.registerReceiver(CONNECTIVITY_RECEIVER, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
Intent intent = new Intent(context, ConnectivityMonitorService.class);
|
||||
intent.setAction(ACTION_START);
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of internet availability, whether there is no connection at all,
|
||||
* whether the connection has no usage limit (like most WiFi), or whether this is
|
||||
* a metered connection like most cellular plans or hotspot WiFi connections.
|
||||
*/
|
||||
public static int getNetworkState(Context context) {
|
||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
|
||||
if (cm == null) {
|
||||
return FLAG_NET_UNAVAILABLE;
|
||||
}
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
if (activeNetwork == null || !activeNetwork.isConnected()) {
|
||||
return FLAG_NET_UNAVAILABLE;
|
||||
}
|
||||
|
||||
int networkType = activeNetwork.getType();
|
||||
switch (networkType) {
|
||||
case ConnectivityManager.TYPE_ETHERNET:
|
||||
case ConnectivityManager.TYPE_WIFI:
|
||||
if (Build.VERSION.SDK_INT >= 16 && cm.isActiveNetworkMetered()) {
|
||||
return FLAG_NET_METERED;
|
||||
} else {
|
||||
return FLAG_NET_NO_LIMIT;
|
||||
}
|
||||
default:
|
||||
return FLAG_NET_METERED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (intent != null && ACTION_START.equals(intent.getAction())) {
|
||||
FDroidApp.networkState = getNetworkState(this);
|
||||
ImageLoader.getInstance().denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed());
|
||||
}
|
||||
}
|
||||
}
|
@ -18,55 +18,51 @@
|
||||
|
||||
package org.fdroid.fdroid.privileged.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
|
||||
/**
|
||||
* Represents the permissions difference between the installed APK, and the
|
||||
* update APK represented by {@link Apk}.
|
||||
*/
|
||||
public class AppDiff {
|
||||
|
||||
private final PackageManager pm;
|
||||
public final PackageInfo pkgInfo;
|
||||
public ApplicationInfo installedAppInfo;
|
||||
public final PackageInfo apkPackageInfo;
|
||||
public final ApplicationInfo installedApplicationInfo;
|
||||
|
||||
/**
|
||||
* Constructor based on F-Droids Apk object
|
||||
*/
|
||||
public AppDiff(PackageManager pm, Apk apk) {
|
||||
this.pm = pm;
|
||||
public AppDiff(Context context, Apk apk) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
apkPackageInfo = new PackageInfo();
|
||||
apkPackageInfo.packageName = apk.packageName;
|
||||
apkPackageInfo.applicationInfo = new ApplicationInfo();
|
||||
apkPackageInfo.requestedPermissions = apk.requestedPermissions;
|
||||
|
||||
pkgInfo = new PackageInfo();
|
||||
pkgInfo.packageName = apk.packageName;
|
||||
pkgInfo.applicationInfo = new ApplicationInfo();
|
||||
pkgInfo.requestedPermissions = apk.requestedPermissions;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
String pkgName = pkgInfo.packageName;
|
||||
String packageName = apkPackageInfo.packageName;
|
||||
// Check if there is already a package on the device with this name
|
||||
// but it has been renamed to something else.
|
||||
final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{pkgName});
|
||||
final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{packageName});
|
||||
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
|
||||
pkgName = oldName[0];
|
||||
pkgInfo.packageName = pkgName;
|
||||
pkgInfo.applicationInfo.packageName = pkgName;
|
||||
packageName = oldName[0];
|
||||
apkPackageInfo.packageName = packageName;
|
||||
apkPackageInfo.applicationInfo.packageName = packageName;
|
||||
}
|
||||
// Check if package is already installed
|
||||
ApplicationInfo applicationInfo;
|
||||
try {
|
||||
// This is a little convoluted because we want to get all uninstalled
|
||||
// apps, but this may include apps with just data, and if it is just
|
||||
// data we still want to count it as "installed".
|
||||
//noinspection WrongConstant (lint is actually wrong here!)
|
||||
installedAppInfo = pm.getApplicationInfo(pkgName,
|
||||
PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
if ((installedAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
|
||||
installedAppInfo = null;
|
||||
applicationInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
|
||||
if ((applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
|
||||
applicationInfo = null;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
installedAppInfo = null;
|
||||
}
|
||||
applicationInfo = null;
|
||||
}
|
||||
installedApplicationInfo = applicationInfo;
|
||||
}
|
||||
}
|
||||
|
@ -92,9 +92,9 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
|
||||
scrollView = null;
|
||||
okCanInstall = false;
|
||||
int msg = 0;
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.pkgInfo);
|
||||
if (appDiff.installedAppInfo != null) {
|
||||
msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.apkPackageInfo);
|
||||
if (appDiff.installedApplicationInfo != null) {
|
||||
msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
? R.string.install_confirm_update_system
|
||||
: R.string.install_confirm_update;
|
||||
scrollView = new CaffeinatedScrollView(this);
|
||||
@ -131,10 +131,10 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
|
||||
}
|
||||
|
||||
if (!permVisible) {
|
||||
if (appDiff.installedAppInfo != null) {
|
||||
if (appDiff.installedApplicationInfo != null) {
|
||||
// This is an update to an application, but there are no
|
||||
// permissions at all.
|
||||
msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
? R.string.install_confirm_update_system_no_perms
|
||||
: R.string.install_confirm_update_no_perms;
|
||||
} else {
|
||||
@ -182,7 +182,7 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel
|
||||
app = AppProvider.Helper.findSpecificApp(getContentResolver(),
|
||||
apk.packageName, apk.repoId, Schema.AppMetadataTable.Cols.ALL);
|
||||
|
||||
appDiff = new AppDiff(getPackageManager(), apk);
|
||||
appDiff = new AppDiff(this, apk);
|
||||
|
||||
setContentView(R.layout.install_start);
|
||||
|
||||
|
@ -789,8 +789,8 @@ public class AppDetailsRecyclerViewAdapter
|
||||
headerView.setText(R.string.permissions);
|
||||
updateExpandableItem(false);
|
||||
contentView.removeAllViews();
|
||||
AppDiff appDiff = new AppDiff(context.getPackageManager(), versions.get(0));
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo);
|
||||
AppDiff appDiff = new AppDiff(context, versions.get(0));
|
||||
AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo);
|
||||
contentView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import android.widget.ImageView;
|
||||
import com.nostra13.universalimageloader.core.DisplayImageOptions;
|
||||
import com.nostra13.universalimageloader.core.ImageLoader;
|
||||
import org.fdroid.fdroid.FDroidApp;
|
||||
import org.fdroid.fdroid.Preferences;
|
||||
import org.fdroid.fdroid.R;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.App;
|
||||
@ -36,6 +37,8 @@ public class ScreenShotsActivity extends AppCompatActivity {
|
||||
private static final String EXTRA_PACKAGE_NAME = "EXTRA_PACKAGE_NAME";
|
||||
private static final String EXTRA_START_POSITION = "EXTRA_START_POSITION";
|
||||
|
||||
private static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance();
|
||||
|
||||
public static Intent getStartIntent(Context context, String packageName, int startPosition) {
|
||||
Intent intent = new Intent(context, ScreenShotsActivity.class);
|
||||
intent.putExtra(EXTRA_PACKAGE_NAME, packageName);
|
||||
@ -68,6 +71,18 @@ public class ScreenShotsActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
IMAGE_LOADER.denyNetworkDownloads(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
IMAGE_LOADER.denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed());
|
||||
}
|
||||
|
||||
private static class ScreenShotPagerAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private final String[] screenshots;
|
||||
|
@ -568,4 +568,12 @@
|
||||
<string name="force_touch_apps">Inclue sas aplicatziones chi impreant s\'ischermu tàtile</string>
|
||||
<string name="force_touch_apps_on">Ammustra sas aplicatziones chi tenent bisòngiu de s\'ischermu tàtile in cale si siat dispositivu</string>
|
||||
|
||||
<string name="repo_add_mirror">Annanghe un\'ispigru</string>
|
||||
<string name="repo_exists_add_fingerprint">%1$s est giai impostadu, custu at a annànghere informatziones de crae noas.</string>
|
||||
<string name="repo_exists_enable">%1$s est giai impostadu, cunfirma chi lu cheres torrare a abilitare.</string>
|
||||
<string name="repo_exists_and_enabled">%1$s est giai impostadu e abilitadu.</string>
|
||||
<string name="repo_delete_to_overwrite">In antis iscantzella %1$s pro annànghere custu cun una crae in cunflitu.</string>
|
||||
<string name="repo_exists_add_mirror">Custa est una còpia de %1$s, lu cheres annànghere comente isprigu?</string>
|
||||
<string name="repo_official_mirrors">Isprigos ufitziales</string>
|
||||
<string name="repo_user_mirrors">Isprigos impreadores</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user