Merge branch 'bug-fixes-1.7' into 'master'
Bug fixes 1.7 Closes #1678 and #1757 See merge request fdroid/fdroidclient!820
This commit is contained in:
		
						commit
						fac36457ea
					
				@ -42,6 +42,7 @@
 | 
				
			|||||||
            android:id="@+id/find_people_button"
 | 
					            android:id="@+id/find_people_button"
 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:maxEms="16"
 | 
				
			||||||
            android:text="@string/nearby_splash__find_people_button"
 | 
					            android:text="@string/nearby_splash__find_people_button"
 | 
				
			||||||
            style="@style/DetailsSecondaryButtonStyle"
 | 
					            style="@style/DetailsSecondaryButtonStyle"
 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@+id/title"
 | 
					            app:layout_constraintTop_toBottomOf="@+id/title"
 | 
				
			||||||
@ -85,6 +86,7 @@
 | 
				
			|||||||
            android:id="@+id/request_read_external_storage_button"
 | 
					            android:id="@+id/request_read_external_storage_button"
 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:maxEms="16"
 | 
				
			||||||
            android:text="@string/nearby_splash__request_permission"
 | 
					            android:text="@string/nearby_splash__request_permission"
 | 
				
			||||||
            style="@style/DetailsSecondaryButtonStyle"
 | 
					            style="@style/DetailsSecondaryButtonStyle"
 | 
				
			||||||
            app:layout_constraintEnd_toEndOf="parent"
 | 
					            app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,9 @@
 | 
				
			|||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:backgroundTint="@color/swap_light_blue"
 | 
					            android:backgroundTint="@color/swap_light_blue"
 | 
				
			||||||
            android:textColor="@android:color/white"
 | 
					            android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					            android:maxEms="10"
 | 
				
			||||||
 | 
					            android:ellipsize="end"
 | 
				
			||||||
 | 
					            android:singleLine="true"
 | 
				
			||||||
            android:text="@string/menu_install"
 | 
					            android:text="@string/menu_install"
 | 
				
			||||||
            tools:ignore="UnusedAttribute" />
 | 
					            tools:ignore="UnusedAttribute" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -28,13 +28,11 @@ public class DeleteCacheService extends JobIntentService {
 | 
				
			|||||||
        Log.w(TAG, "Deleting all cached contents!");
 | 
					        Log.w(TAG, "Deleting all cached contents!");
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            File cacheDir = getCacheDir();
 | 
					            File cacheDir = getCacheDir();
 | 
				
			||||||
            if (cacheDir != null) {
 | 
					            FileUtils.deleteDirectory(cacheDir);
 | 
				
			||||||
                FileUtils.deleteDirectory(cacheDir);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            for (File dir : ContextCompat.getExternalCacheDirs(this)) {
 | 
					            for (File dir : ContextCompat.getExternalCacheDirs(this)) {
 | 
				
			||||||
                FileUtils.deleteDirectory(dir);
 | 
					                FileUtils.deleteDirectory(dir);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Throwable e) { // NOPMD
 | 
				
			||||||
            // ignored
 | 
					            // ignored
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -414,7 +414,7 @@ public class IndexUpdater {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Utils.debugLog(TAG, "Saving new signing certificate in the database for " + repo.address);
 | 
					        Utils.debugLog(TAG, "Saving new signing certificate in the database for " + repo.address);
 | 
				
			||||||
        ContentValues values = new ContentValues(2);
 | 
					        ContentValues values = new ContentValues(2);
 | 
				
			||||||
        values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatDate(new Date(), ""));
 | 
					        values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
 | 
				
			||||||
        values.put(RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar));
 | 
					        values.put(RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar));
 | 
				
			||||||
        RepoProvider.Helper.update(context, repo, values);
 | 
					        RepoProvider.Helper.update(context, repo, values);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -470,7 +470,7 @@ public class IndexV1Updater extends IndexUpdater {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            Utils.debugLog(TAG, "Saving new signing certificate to database for " + repo.address);
 | 
					            Utils.debugLog(TAG, "Saving new signing certificate to database for " + repo.address);
 | 
				
			||||||
            ContentValues values = new ContentValues(2);
 | 
					            ContentValues values = new ContentValues(2);
 | 
				
			||||||
            values.put(Schema.RepoTable.Cols.LAST_UPDATED, Utils.formatDate(new Date(), ""));
 | 
					            values.put(Schema.RepoTable.Cols.LAST_UPDATED, Utils.formatTime(new Date(), ""));
 | 
				
			||||||
            values.put(Schema.RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar));
 | 
					            values.put(Schema.RepoTable.Cols.SIGNING_CERT, Hasher.hex(rawCertFromJar));
 | 
				
			||||||
            RepoProvider.Helper.update(context, repo, values);
 | 
					            RepoProvider.Helper.update(context, repo, values);
 | 
				
			||||||
            repo.signingCertificate = certFromJar;
 | 
					            repo.signingCertificate = certFromJar;
 | 
				
			||||||
 | 
				
			|||||||
@ -236,7 +236,7 @@ public class UpdateService extends JobIntentService {
 | 
				
			|||||||
                    Utils.debugLog(TAG, "scheduling update because there is good internet");
 | 
					                    Utils.debugLog(TAG, "scheduling update because there is good internet");
 | 
				
			||||||
                    schedule(context);
 | 
					                    schedule(context);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch (Exception e) {
 | 
					            } catch (Throwable e) { // NOPMD
 | 
				
			||||||
                Utils.debugLog(TAG, e.getMessage());
 | 
					                Utils.debugLog(TAG, e.getMessage());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            isScheduleIfStillOnWifiRunning = false;
 | 
					            isScheduleIfStillOnWifiRunning = false;
 | 
				
			||||||
 | 
				
			|||||||
@ -79,6 +79,7 @@ import java.util.HashMap;
 | 
				
			|||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
import java.util.concurrent.TimeUnit;
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
import java.util.regex.Pattern;
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -96,6 +97,8 @@ public final class Utils {
 | 
				
			|||||||
    private static final SimpleDateFormat TIME_FORMAT =
 | 
					    private static final SimpleDateFormat TIME_FORMAT =
 | 
				
			||||||
            new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.ENGLISH);
 | 
					            new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss", Locale.ENGLISH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final TimeZone UTC = TimeZone.getTimeZone("Etc/GMT");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static final String[] FRIENDLY_SIZE_FORMAT = {
 | 
					    private static final String[] FRIENDLY_SIZE_FORMAT = {
 | 
				
			||||||
            "%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB",
 | 
					            "%.0f B", "%.0f KiB", "%.1f MiB", "%.2f GiB",
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
@ -384,7 +387,7 @@ public final class Utils {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            ret = formatter.toString();
 | 
					            ret = formatter.toString();
 | 
				
			||||||
            formatter.close();
 | 
					            formatter.close();
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Throwable e) { // NOPMD
 | 
				
			||||||
            Log.w(TAG, "Unable to get certificate fingerprint", e);
 | 
					            Log.w(TAG, "Unable to get certificate fingerprint", e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
@ -583,6 +586,7 @@ public final class Utils {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        Date result;
 | 
					        Date result;
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
 | 
					            format.setTimeZone(UTC);
 | 
				
			||||||
            result = format.parse(str);
 | 
					            result = format.parse(str);
 | 
				
			||||||
        } catch (ArrayIndexOutOfBoundsException | NumberFormatException | ParseException e) {
 | 
					        } catch (ArrayIndexOutOfBoundsException | NumberFormatException | ParseException e) {
 | 
				
			||||||
            e.printStackTrace();
 | 
					            e.printStackTrace();
 | 
				
			||||||
@ -595,21 +599,34 @@ public final class Utils {
 | 
				
			|||||||
        if (date == null) {
 | 
					        if (date == null) {
 | 
				
			||||||
            return fallback;
 | 
					            return fallback;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        format.setTimeZone(UTC);
 | 
				
			||||||
        return format.format(date);
 | 
					        return format.format(date);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parses a date string into UTC time
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static Date parseDate(String str, Date fallback) {
 | 
					    public static Date parseDate(String str, Date fallback) {
 | 
				
			||||||
        return parseDateFormat(DATE_FORMAT, str, fallback);
 | 
					        return parseDateFormat(DATE_FORMAT, str, fallback);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Formats UTC time into a date string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String formatDate(Date date, String fallback) {
 | 
					    public static String formatDate(Date date, String fallback) {
 | 
				
			||||||
        return formatDateFormat(DATE_FORMAT, date, fallback);
 | 
					        return formatDateFormat(DATE_FORMAT, date, fallback);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Parses a date/time string into UTC time
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static Date parseTime(String str, Date fallback) {
 | 
					    public static Date parseTime(String str, Date fallback) {
 | 
				
			||||||
        return parseDateFormat(TIME_FORMAT, str, fallback);
 | 
					        return parseDateFormat(TIME_FORMAT, str, fallback);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Formats UTC time into a date/time string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String formatTime(Date date, String fallback) {
 | 
					    public static String formatTime(Date date, String fallback) {
 | 
				
			||||||
        return formatDateFormat(TIME_FORMAT, date, fallback);
 | 
					        return formatDateFormat(TIME_FORMAT, date, fallback);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -165,7 +165,8 @@ public class Repo extends ValueObject {
 | 
				
			|||||||
                    inuse = cursor.getInt(i) == 1;
 | 
					                    inuse = cursor.getInt(i) == 1;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case Cols.LAST_UPDATED:
 | 
					                case Cols.LAST_UPDATED:
 | 
				
			||||||
                    lastUpdated = Utils.parseTime(cursor.getString(i), null);
 | 
					                    String dateString = cursor.getString(i);
 | 
				
			||||||
 | 
					                    lastUpdated = Utils.parseTime(dateString, Utils.parseDate(dateString, null));
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                case Cols.MAX_AGE:
 | 
					                case Cols.MAX_AGE:
 | 
				
			||||||
                    maxage = cursor.getInt(i);
 | 
					                    maxage = cursor.getInt(i);
 | 
				
			||||||
@ -296,7 +297,7 @@ public class Repo extends ValueObject {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        if (values.containsKey(Cols.LAST_UPDATED)) {
 | 
					        if (values.containsKey(Cols.LAST_UPDATED)) {
 | 
				
			||||||
            final String dateString = values.getAsString(Cols.LAST_UPDATED);
 | 
					            final String dateString = values.getAsString(Cols.LAST_UPDATED);
 | 
				
			||||||
            lastUpdated = Utils.parseTime(dateString, null);
 | 
					            lastUpdated = Utils.parseTime(dateString, Utils.parseDate(dateString, null));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (values.containsKey(Cols.MAX_AGE)) {
 | 
					        if (values.containsKey(Cols.MAX_AGE)) {
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,6 @@ import android.support.annotation.NonNull;
 | 
				
			|||||||
import android.support.annotation.Nullable;
 | 
					import android.support.annotation.Nullable;
 | 
				
			||||||
import android.text.TextUtils;
 | 
					import android.text.TextUtils;
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					 | 
				
			||||||
import org.fdroid.fdroid.AppUpdateStatusManager;
 | 
					import org.fdroid.fdroid.AppUpdateStatusManager;
 | 
				
			||||||
import org.fdroid.fdroid.Utils;
 | 
					import org.fdroid.fdroid.Utils;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
					import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
				
			||||||
@ -278,7 +277,8 @@ public class RepoProvider extends FDroidProvider {
 | 
				
			|||||||
            if (cursor != null) {
 | 
					            if (cursor != null) {
 | 
				
			||||||
                if (cursor.getCount() > 0) {
 | 
					                if (cursor.getCount() > 0) {
 | 
				
			||||||
                    cursor.moveToFirst();
 | 
					                    cursor.moveToFirst();
 | 
				
			||||||
                    lastUpdate = Utils.parseDate(cursor.getString(0), null);
 | 
					                    String dateString = cursor.getString(0);
 | 
				
			||||||
 | 
					                    lastUpdate = Utils.parseTime(dateString, Utils.parseDate(dateString, null));
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                cursor.close();
 | 
					                cursor.close();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -265,7 +265,7 @@ public class PrivilegedInstaller extends Installer {
 | 
				
			|||||||
        PackageManager pm = context.getPackageManager();
 | 
					        PackageManager pm = context.getPackageManager();
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            pm.getPackageInfo(PRIVILEGED_EXTENSION_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
 | 
					            pm.getPackageInfo(PRIVILEGED_EXTENSION_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
 | 
				
			||||||
            return true;
 | 
					            return pm.getApplicationInfo(PRIVILEGED_EXTENSION_PACKAGE_NAME, 0).enabled;
 | 
				
			||||||
        } catch (PackageManager.NameNotFoundException e) {
 | 
					        } catch (PackageManager.NameNotFoundException e) {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -555,11 +555,16 @@ public class AppDetailsRecyclerViewAdapter
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            updateAntiFeaturesWarning();
 | 
					            updateAntiFeaturesWarning();
 | 
				
			||||||
            buttonSecondaryView.setText(R.string.menu_uninstall);
 | 
					 | 
				
			||||||
            buttonSecondaryView.setVisibility(app.isUninstallable(context) ? View.VISIBLE : View.INVISIBLE);
 | 
					 | 
				
			||||||
            buttonSecondaryView.setOnClickListener(onUnInstallClickListener);
 | 
					 | 
				
			||||||
            buttonPrimaryView.setText(R.string.menu_install);
 | 
					            buttonPrimaryView.setText(R.string.menu_install);
 | 
				
			||||||
            buttonPrimaryView.setVisibility(versions.size() > 0 ? View.VISIBLE : View.GONE);
 | 
					            buttonPrimaryView.setVisibility(versions.size() > 0 ? View.VISIBLE : View.GONE);
 | 
				
			||||||
 | 
					            buttonSecondaryView.setText(R.string.menu_uninstall);
 | 
				
			||||||
 | 
					            buttonSecondaryView.setVisibility(app.isUninstallable(context) ? View.VISIBLE : View.INVISIBLE);
 | 
				
			||||||
 | 
					            buttonSecondaryView.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onClick(View v) {
 | 
				
			||||||
 | 
					                    callbacks.uninstallApk();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
            if (callbacks.isAppDownloading()) {
 | 
					            if (callbacks.isAppDownloading()) {
 | 
				
			||||||
                buttonPrimaryView.setText(R.string.downloading);
 | 
					                buttonPrimaryView.setText(R.string.downloading);
 | 
				
			||||||
                buttonPrimaryView.setEnabled(false);
 | 
					                buttonPrimaryView.setEnabled(false);
 | 
				
			||||||
@ -568,22 +573,37 @@ public class AppDetailsRecyclerViewAdapter
 | 
				
			|||||||
            } else if (!app.isInstalled(context) && suggestedApk != null) {
 | 
					            } else if (!app.isInstalled(context) && suggestedApk != null) {
 | 
				
			||||||
                // Check count > 0 due to incompatible apps resulting in an empty list.
 | 
					                // Check count > 0 due to incompatible apps resulting in an empty list.
 | 
				
			||||||
                callbacks.disableAndroidBeam();
 | 
					                callbacks.disableAndroidBeam();
 | 
				
			||||||
 | 
					                progressLayout.setVisibility(View.GONE);
 | 
				
			||||||
                // Set Install button and hide second button
 | 
					                // Set Install button and hide second button
 | 
				
			||||||
                buttonPrimaryView.setText(R.string.menu_install);
 | 
					                buttonPrimaryView.setText(R.string.menu_install);
 | 
				
			||||||
                buttonPrimaryView.setOnClickListener(onInstallClickListener);
 | 
					 | 
				
			||||||
                buttonPrimaryView.setEnabled(true);
 | 
					                buttonPrimaryView.setEnabled(true);
 | 
				
			||||||
                buttonLayout.setVisibility(View.VISIBLE);
 | 
					                buttonLayout.setVisibility(View.VISIBLE);
 | 
				
			||||||
                progressLayout.setVisibility(View.GONE);
 | 
					                buttonPrimaryView.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                    @Override
 | 
				
			||||||
 | 
					                    public void onClick(View v) {
 | 
				
			||||||
 | 
					                        callbacks.installApk();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
            } else if (app.isInstalled(context)) {
 | 
					            } else if (app.isInstalled(context)) {
 | 
				
			||||||
                callbacks.enableAndroidBeam();
 | 
					                callbacks.enableAndroidBeam();
 | 
				
			||||||
                if (app.canAndWantToUpdate(context) && suggestedApk != null) {
 | 
					                if (app.canAndWantToUpdate(context) && suggestedApk != null) {
 | 
				
			||||||
                    buttonPrimaryView.setText(R.string.menu_upgrade);
 | 
					                    buttonPrimaryView.setText(R.string.menu_upgrade);
 | 
				
			||||||
                    buttonPrimaryView.setOnClickListener(onUpgradeClickListener);
 | 
					                    buttonPrimaryView.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                        @Override
 | 
				
			||||||
 | 
					                        public void onClick(View v) {
 | 
				
			||||||
 | 
					                            callbacks.installApk();
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Apk mediaApk = app.getMediaApkifInstalled(context);
 | 
					                    Apk mediaApk = app.getMediaApkifInstalled(context);
 | 
				
			||||||
                    if (context.getPackageManager().getLaunchIntentForPackage(app.packageName) != null) {
 | 
					                    if (context.getPackageManager().getLaunchIntentForPackage(app.packageName) != null) {
 | 
				
			||||||
                        buttonPrimaryView.setText(R.string.menu_launch);
 | 
					                        buttonPrimaryView.setText(R.string.menu_launch);
 | 
				
			||||||
                        buttonPrimaryView.setOnClickListener(onLaunchClickListener);
 | 
					                        buttonPrimaryView.setOnClickListener(new View.OnClickListener() {
 | 
				
			||||||
 | 
					                            @Override
 | 
				
			||||||
 | 
					                            public void onClick(View v) {
 | 
				
			||||||
 | 
					                                callbacks.launchApk();
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
                    } else if (!app.isApk && mediaApk != null) {
 | 
					                    } else if (!app.isApk && mediaApk != null) {
 | 
				
			||||||
                        final File installedFile = new File(mediaApk.getMediaInstallPath(context), mediaApk.apkName);
 | 
					                        final File installedFile = new File(mediaApk.getMediaInstallPath(context), mediaApk.apkName);
 | 
				
			||||||
                        if (!installedFile.toString().startsWith(context.getApplicationInfo().dataDir)) {
 | 
					                        if (!installedFile.toString().startsWith(context.getApplicationInfo().dataDir)) {
 | 
				
			||||||
@ -1324,34 +1344,6 @@ public class AppDetailsRecyclerViewAdapter
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final View.OnClickListener onInstallClickListener = new View.OnClickListener() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onClick(View v) {
 | 
					 | 
				
			||||||
            callbacks.installApk();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final View.OnClickListener onUnInstallClickListener = new View.OnClickListener() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onClick(View v) {
 | 
					 | 
				
			||||||
            callbacks.uninstallApk();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final View.OnClickListener onUpgradeClickListener = new View.OnClickListener() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onClick(View v) {
 | 
					 | 
				
			||||||
            callbacks.installApk();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final View.OnClickListener onLaunchClickListener = new View.OnClickListener() {
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        public void onClick(View v) {
 | 
					 | 
				
			||||||
            callbacks.launchApk();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private boolean uriIsSetAndCanBeOpened(String s) {
 | 
					    private boolean uriIsSetAndCanBeOpened(String s) {
 | 
				
			||||||
        if (TextUtils.isEmpty(s)) {
 | 
					        if (TextUtils.isEmpty(s)) {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
 | 
				
			|||||||
@ -143,6 +143,9 @@
 | 
				
			|||||||
                android:layout_marginTop="5dp"
 | 
					                android:layout_marginTop="5dp"
 | 
				
			||||||
                android:layout_marginRight="4dp"
 | 
					                android:layout_marginRight="4dp"
 | 
				
			||||||
                android:layout_marginEnd="4dp"
 | 
					                android:layout_marginEnd="4dp"
 | 
				
			||||||
 | 
					                android:maxEms="10"
 | 
				
			||||||
 | 
					                android:ellipsize="end"
 | 
				
			||||||
 | 
					                android:singleLine="true"
 | 
				
			||||||
                tools:text="@string/menu_install"/>
 | 
					                tools:text="@string/menu_install"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Button
 | 
					            <Button
 | 
				
			||||||
 | 
				
			|||||||
@ -42,6 +42,7 @@
 | 
				
			|||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_marginEnd="16dp"
 | 
					        android:layout_marginEnd="16dp"
 | 
				
			||||||
        android:layout_marginRight="16dp"
 | 
					        android:layout_marginRight="16dp"
 | 
				
			||||||
 | 
					        android:maxEms="10"
 | 
				
			||||||
        android:text="@string/update_all"
 | 
					        android:text="@string/update_all"
 | 
				
			||||||
        style="@style/DetailsPrimaryButtonStyle"
 | 
					        style="@style/DetailsPrimaryButtonStyle"
 | 
				
			||||||
        app:layout_constraintEnd_toEndOf="parent"
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
@ -54,6 +55,8 @@
 | 
				
			|||||||
        android:layout_width="wrap_content"
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_margin="8dp"
 | 
					        android:layout_margin="8dp"
 | 
				
			||||||
 | 
					        android:ellipsize="middle"
 | 
				
			||||||
 | 
					        android:singleLine="true"
 | 
				
			||||||
        tools:text="Show apps"
 | 
					        tools:text="Show apps"
 | 
				
			||||||
        app:layout_constraintStart_toStartOf="parent"
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
        app:layout_constraintEnd_toEndOf="parent"
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
				
			|||||||
@ -10,6 +10,8 @@ import org.robolectric.RuntimeEnvironment;
 | 
				
			|||||||
import org.robolectric.annotation.Config;
 | 
					import org.robolectric.annotation.Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
import static org.junit.Assert.assertFalse;
 | 
					import static org.junit.Assert.assertFalse;
 | 
				
			||||||
@ -77,9 +79,9 @@ public class UtilsTest {
 | 
				
			|||||||
        assertEquals("three", tripleValue[2]);
 | 
					        assertEquals("three", tripleValue[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        assertNull(Utils.serializeCommaSeparatedString(null));
 | 
					        assertNull(Utils.serializeCommaSeparatedString(null));
 | 
				
			||||||
        assertNull(Utils.serializeCommaSeparatedString(new String[] {}));
 | 
					        assertNull(Utils.serializeCommaSeparatedString(new String[]{}));
 | 
				
			||||||
        assertEquals("Single", Utils.serializeCommaSeparatedString(new String[] {"Single"}));
 | 
					        assertEquals("Single", Utils.serializeCommaSeparatedString(new String[]{"Single"}));
 | 
				
			||||||
        assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[] {"One", "TWO", "three"}));
 | 
					        assertEquals("One,TWO,three", Utils.serializeCommaSeparatedString(new String[]{"One", "TWO", "three"}));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
@ -192,4 +194,25 @@ public class UtilsTest {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    // TODO write tests that work with a Certificate
 | 
					    // TODO write tests that work with a Certificate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    public void testIndexDatesWithTimeZones() {
 | 
				
			||||||
 | 
					        for (int h = 0; h < 12; h++) {
 | 
				
			||||||
 | 
					            for (int m = 0; m < 60; m = m + 15) {
 | 
				
			||||||
 | 
					                TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                String timeString = "2017-11-27_20:13:24";
 | 
				
			||||||
 | 
					                Date time = Utils.parseTime(timeString, null);
 | 
				
			||||||
 | 
					                assertEquals("The String representation must match", timeString, Utils.formatTime(time, null));
 | 
				
			||||||
 | 
					                assertEquals(timeString + " failed to parse", 1511813604000L, time.getTime());
 | 
				
			||||||
 | 
					                assertEquals("time zones should match", -((h * 60) + m), time.getTimezoneOffset());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT+%d%02d", h, m)));
 | 
				
			||||||
 | 
					                String dateString = "2017-11-27";
 | 
				
			||||||
 | 
					                Date date = Utils.parseDate(dateString, null);
 | 
				
			||||||
 | 
					                assertEquals("The String representation must match", dateString, Utils.formatDate(date, null));
 | 
				
			||||||
 | 
					                assertEquals(dateString + " failed to parse", 1511740800000L, date.getTime());
 | 
				
			||||||
 | 
					                assertEquals("time zones should match", -((h * 60) + m), date.getTimezoneOffset());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,10 +7,12 @@ import android.net.Uri;
 | 
				
			|||||||
import org.fdroid.fdroid.Assert;
 | 
					import org.fdroid.fdroid.Assert;
 | 
				
			||||||
import org.fdroid.fdroid.BuildConfig;
 | 
					import org.fdroid.fdroid.BuildConfig;
 | 
				
			||||||
import org.fdroid.fdroid.TestUtils;
 | 
					import org.fdroid.fdroid.TestUtils;
 | 
				
			||||||
 | 
					import org.fdroid.fdroid.Utils;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
 | 
					import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
					import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
				
			||||||
import org.fdroid.fdroid.mock.MockApk;
 | 
					import org.fdroid.fdroid.mock.MockApk;
 | 
				
			||||||
import org.fdroid.fdroid.mock.MockRepo;
 | 
					import org.fdroid.fdroid.mock.MockRepo;
 | 
				
			||||||
 | 
					import org.junit.BeforeClass;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
@ -18,6 +20,7 @@ import org.robolectric.annotation.Config;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.Date;
 | 
					import java.util.Date;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.fdroid.fdroid.Assert.assertCantDelete;
 | 
					import static org.fdroid.fdroid.Assert.assertCantDelete;
 | 
				
			||||||
import static org.fdroid.fdroid.Assert.assertResultCount;
 | 
					import static org.fdroid.fdroid.Assert.assertResultCount;
 | 
				
			||||||
@ -34,6 +37,13 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final String[] PROJ = Cols.ALL;
 | 
					    private static final String[] PROJ = Cols.ALL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @BeforeClass
 | 
				
			||||||
 | 
					    public static void setRandomTimeZone() {
 | 
				
			||||||
 | 
					        TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
 | 
				
			||||||
 | 
					                System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
 | 
				
			||||||
 | 
					        System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testAppApks() {
 | 
					    public void testAppApks() {
 | 
				
			||||||
        App fdroidApp = insertApp(context, "org.fdroid.fdroid", "F-Droid");
 | 
					        App fdroidApp = insertApp(context, "org.fdroid.fdroid", "F-Droid");
 | 
				
			||||||
@ -153,7 +163,7 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testCount() {
 | 
					    public void testCount() {
 | 
				
			||||||
        String[] projectionCount = new String[] {Cols._COUNT};
 | 
					        String[] projectionCount = new String[]{Cols._COUNT};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (int i = 0; i < 13; i++) {
 | 
					        for (int i = 0; i < 13; i++) {
 | 
				
			||||||
            Assert.insertApk(context, "com.example", i);
 | 
					            Assert.insertApk(context, "com.example", i);
 | 
				
			||||||
@ -315,12 +325,13 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
        assertNull(apk.added);
 | 
					        assertNull(apk.added);
 | 
				
			||||||
        assertNull(apk.hashType);
 | 
					        assertNull(apk.hashType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        apk.antiFeatures = new String[] {"KnownVuln", "Other anti feature"};
 | 
					        apk.antiFeatures = new String[]{"KnownVuln", "Other anti feature"};
 | 
				
			||||||
        apk.features = new String[] {"one", "two", "three" };
 | 
					        apk.features = new String[]{"one", "two", "three"};
 | 
				
			||||||
        long dateTimestamp = System.currentTimeMillis();
 | 
					 | 
				
			||||||
        apk.added = new Date(dateTimestamp);
 | 
					 | 
				
			||||||
        apk.hashType = "i'm a hash type";
 | 
					        apk.hashType = "i'm a hash type";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Date testTime = Utils.parseDate(Utils.formatTime(new Date(System.currentTimeMillis()), null), null);
 | 
				
			||||||
 | 
					        apk.added = testTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ApkProvider.Helper.update(context, apk);
 | 
					        ApkProvider.Helper.update(context, apk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Should not have inserted anything else, just updated the already existing apk.
 | 
					        // Should not have inserted anything else, just updated the already existing apk.
 | 
				
			||||||
@ -340,9 +351,10 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        assertArrayEquals(new String[]{"KnownVuln", "Other anti feature"}, updatedApk.antiFeatures);
 | 
					        assertArrayEquals(new String[]{"KnownVuln", "Other anti feature"}, updatedApk.antiFeatures);
 | 
				
			||||||
        assertArrayEquals(new String[]{"one", "two", "three"}, updatedApk.features);
 | 
					        assertArrayEquals(new String[]{"one", "two", "three"}, updatedApk.features);
 | 
				
			||||||
        assertEquals(new Date(dateTimestamp).getYear(), updatedApk.added.getYear());
 | 
					        assertEquals(testTime.getYear(), updatedApk.added.getYear());
 | 
				
			||||||
        assertEquals(new Date(dateTimestamp).getMonth(), updatedApk.added.getMonth());
 | 
					        assertEquals(testTime.getYear(), updatedApk.added.getYear());
 | 
				
			||||||
        assertEquals(new Date(dateTimestamp).getDay(), updatedApk.added.getDay());
 | 
					        assertEquals(testTime.getMonth(), updatedApk.added.getMonth());
 | 
				
			||||||
 | 
					        assertEquals(testTime.getDay(), updatedApk.added.getDay());
 | 
				
			||||||
        assertEquals("i'm a hash type", updatedApk.hashType);
 | 
					        assertEquals("i'm a hash type", updatedApk.hashType);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -381,8 +393,8 @@ public class ApkProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
        assertEquals("a hash type", apk.hashType);
 | 
					        assertEquals("a hash type", apk.hashType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        String[] projection = {
 | 
					        String[] projection = {
 | 
				
			||||||
            Cols.Package.PACKAGE_NAME,
 | 
					                Cols.Package.PACKAGE_NAME,
 | 
				
			||||||
            Cols.HASH,
 | 
					                Cols.HASH,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection);
 | 
					        Apk apkLessFields = ApkProvider.Helper.findApkFromAnyRepo(context, "com.example", 11, null, projection);
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,7 @@ import org.fdroid.fdroid.Preferences;
 | 
				
			|||||||
import org.fdroid.fdroid.TestUtils;
 | 
					import org.fdroid.fdroid.TestUtils;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
 | 
					import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
 | 
				
			||||||
import org.junit.Before;
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.BeforeClass;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
@ -19,6 +20,7 @@ import org.robolectric.shadows.ShadowContentResolver;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.fdroid.fdroid.Assert.assertContainsOnly;
 | 
					import static org.fdroid.fdroid.Assert.assertContainsOnly;
 | 
				
			||||||
import static org.fdroid.fdroid.Assert.assertResultCount;
 | 
					import static org.fdroid.fdroid.Assert.assertResultCount;
 | 
				
			||||||
@ -36,6 +38,13 @@ public class AppProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final String[] PROJ = Cols.ALL;
 | 
					    private static final String[] PROJ = Cols.ALL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @BeforeClass
 | 
				
			||||||
 | 
					    public static void setRandomTimeZone() {
 | 
				
			||||||
 | 
					        TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
 | 
				
			||||||
 | 
					                System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
 | 
				
			||||||
 | 
					        System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Before
 | 
					    @Before
 | 
				
			||||||
    public void setup() {
 | 
					    public void setup() {
 | 
				
			||||||
        TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
 | 
					        TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ import org.fdroid.fdroid.BuildConfig;
 | 
				
			|||||||
import org.fdroid.fdroid.R;
 | 
					import org.fdroid.fdroid.R;
 | 
				
			||||||
import org.fdroid.fdroid.Utils;
 | 
					import org.fdroid.fdroid.Utils;
 | 
				
			||||||
import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
					import org.fdroid.fdroid.data.Schema.RepoTable;
 | 
				
			||||||
 | 
					import org.junit.BeforeClass;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
@ -37,6 +38,7 @@ import org.robolectric.annotation.Config;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.Date;
 | 
					import java.util.Date;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
import static org.junit.Assert.assertNotNull;
 | 
					import static org.junit.Assert.assertNotNull;
 | 
				
			||||||
@ -48,6 +50,16 @@ public class RepoProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final String[] COLS = RepoTable.Cols.ALL;
 | 
					    private static final String[] COLS = RepoTable.Cols.ALL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set to random time zone to make sure that the dates are properly parsed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @BeforeClass
 | 
				
			||||||
 | 
					    public static void setRandomTimeZone() {
 | 
				
			||||||
 | 
					        TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
 | 
				
			||||||
 | 
					                System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
 | 
				
			||||||
 | 
					        System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void countEnabledRepos() {
 | 
					    public void countEnabledRepos() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -95,7 +107,7 @@ public class RepoProviderTest extends FDroidProviderTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private Repo setLastUpdate(Repo repo, Date date) {
 | 
					    private Repo setLastUpdate(Repo repo, Date date) {
 | 
				
			||||||
        ContentValues values = new ContentValues(1);
 | 
					        ContentValues values = new ContentValues(1);
 | 
				
			||||||
        values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatDate(date, null));
 | 
					        values.put(RepoTable.Cols.LAST_UPDATED, Utils.formatTime(date, null));
 | 
				
			||||||
        RepoProvider.Helper.update(context, repo, values);
 | 
					        RepoProvider.Helper.update(context, repo, values);
 | 
				
			||||||
        return RepoProvider.Helper.findByAddress(context, repo.address);
 | 
					        return RepoProvider.Helper.findByAddress(context, repo.address);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@ import org.apache.commons.io.FileUtils;
 | 
				
			|||||||
import org.fdroid.fdroid.BuildConfig;
 | 
					import org.fdroid.fdroid.BuildConfig;
 | 
				
			||||||
import org.fdroid.fdroid.mock.MockRepo;
 | 
					import org.fdroid.fdroid.mock.MockRepo;
 | 
				
			||||||
import org.fdroid.fdroid.mock.RepoDetails;
 | 
					import org.fdroid.fdroid.mock.RepoDetails;
 | 
				
			||||||
 | 
					import org.junit.BeforeClass;
 | 
				
			||||||
import org.junit.Test;
 | 
					import org.junit.Test;
 | 
				
			||||||
import org.junit.runner.RunWith;
 | 
					import org.junit.runner.RunWith;
 | 
				
			||||||
import org.robolectric.RobolectricTestRunner;
 | 
					import org.robolectric.RobolectricTestRunner;
 | 
				
			||||||
@ -48,6 +49,7 @@ import java.util.Collections;
 | 
				
			|||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.TimeZone;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.junit.Assert.assertEquals;
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
import static org.junit.Assert.assertFalse;
 | 
					import static org.junit.Assert.assertFalse;
 | 
				
			||||||
@ -63,6 +65,16 @@ public class RepoXMLHandlerTest {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final String FAKE_SIGNING_CERT = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"; // NOCHECKSTYLE LineLength
 | 
					    private static final String FAKE_SIGNING_CERT = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"; // NOCHECKSTYLE LineLength
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Set to random time zone to make sure that the dates are properly parsed.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @BeforeClass
 | 
				
			||||||
 | 
					    public static void setRandomTimeZone() {
 | 
				
			||||||
 | 
					        TimeZone.setDefault(TimeZone.getTimeZone(String.format("GMT-%d:%02d",
 | 
				
			||||||
 | 
					                System.currentTimeMillis() % 12, System.currentTimeMillis() % 60)));
 | 
				
			||||||
 | 
					        System.out.println("TIME ZONE for this test: " + TimeZone.getDefault());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testExtendedPerms() throws IOException {
 | 
					    public void testExtendedPerms() throws IOException {
 | 
				
			||||||
        Repo expectedRepo = new Repo();
 | 
					        Repo expectedRepo = new Repo();
 | 
				
			||||||
@ -129,6 +141,12 @@ public class RepoXMLHandlerTest {
 | 
				
			|||||||
                "org.gege.caldavsyncadapter",
 | 
					                "org.gege.caldavsyncadapter",
 | 
				
			||||||
                "info.guardianproject.checkey",
 | 
					                "info.guardianproject.checkey",
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        for (App app : actualDetails.apps) {
 | 
				
			||||||
 | 
					            if ("org.mozilla.firefox".equals(app.packageName)) {
 | 
				
			||||||
 | 
					                assertEquals(1411776000000L, app.added.getTime());
 | 
				
			||||||
 | 
					                assertEquals(1411862400000L, app.lastUpdated.getTime());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test(expected = IllegalArgumentException.class)
 | 
					    @Test(expected = IllegalArgumentException.class)
 | 
				
			||||||
@ -897,6 +915,10 @@ public class RepoXMLHandlerTest {
 | 
				
			|||||||
        List<App> apps = actualDetails.apps;
 | 
					        List<App> apps = actualDetails.apps;
 | 
				
			||||||
        assertNotNull(apps);
 | 
					        assertNotNull(apps);
 | 
				
			||||||
        assertEquals(apps.size(), appCount);
 | 
					        assertEquals(apps.size(), appCount);
 | 
				
			||||||
 | 
					        for (App app : apps) {
 | 
				
			||||||
 | 
					            assertTrue("Added should have been set", app.added.getTime() > 0);
 | 
				
			||||||
 | 
					            assertTrue("Last Updated should have been set", app.lastUpdated.getTime() > 0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<Apk> apks = actualDetails.apks;
 | 
					        List<Apk> apks = actualDetails.apks;
 | 
				
			||||||
        assertNotNull(apks);
 | 
					        assertNotNull(apks);
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										48
									
								
								tools/check-string-maxlength.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										48
									
								
								tools/check-string-maxlength.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env python3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Remove extra translations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import glob
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					from xml.etree import ElementTree
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					maxlengths = {
 | 
				
			||||||
 | 
					    "menu_install": 20,
 | 
				
			||||||
 | 
					    "menu_uninstall": 20,
 | 
				
			||||||
 | 
					    "nearby_splash__find_people_button": 30,
 | 
				
			||||||
 | 
					    "nearby_splash__request_permission": 30,
 | 
				
			||||||
 | 
					    "update_all": 20,
 | 
				
			||||||
 | 
					    "updates__hide_updateable_apps": 35,
 | 
				
			||||||
 | 
					    "updates__show_updateable_apps": 35,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					resdir = os.path.join(os.path.dirname(__file__), '..', 'app', 'src', 'main', 'res')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					count = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for d in sorted(glob.glob(os.path.join(resdir, 'values-*'))):
 | 
				
			||||||
 | 
					    locale = d.split('/')[-1][7:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    str_path = os.path.join(d, 'strings.xml')
 | 
				
			||||||
 | 
					    if not os.path.exists(str_path):
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with open(str_path, encoding='utf-8') as fp:
 | 
				
			||||||
 | 
					        fulltext = fp.read()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tree = ElementTree.parse(str_path)
 | 
				
			||||||
 | 
					    root = tree.getroot()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for e in root.findall('.//string'):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if maxlengths.get(e.attrib['name']) is not None \
 | 
				
			||||||
 | 
					           and len(e.text) > maxlengths.get(e.attrib['name']):
 | 
				
			||||||
 | 
					            print(e.attrib['name'], locale, str(len(e.text)) + ':\t\t"' + e.text + '"')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if count > 0:
 | 
				
			||||||
 | 
					    print("%d over-long strings found!" % count)
 | 
				
			||||||
 | 
					    sys.exit(count)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										82
									
								
								tools/pick-complete-translations.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										82
									
								
								tools/pick-complete-translations.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/python3
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# cherry-pick complete translations from weblate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import git
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_paths_tuple(locale):
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        'metadata/%s/*.txt' % locale,
 | 
				
			||||||
 | 
					        'metadata/%s/changelogs/*.txt' % locale,
 | 
				
			||||||
 | 
					        'app/src/main/res/values-%s/strings.xml' % re.sub(r'-', r'-r', locale),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					projectbasedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 | 
				
			||||||
 | 
					print(projectbasedir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					repo = git.Repo(projectbasedir)
 | 
				
			||||||
 | 
					weblate = repo.remotes.weblate
 | 
				
			||||||
 | 
					weblate.fetch()
 | 
				
			||||||
 | 
					upstream = repo.remotes.upstream
 | 
				
			||||||
 | 
					upstream.fetch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					url = 'https://hosted.weblate.org/exports/stats/f-droid/f-droid/?format=json'
 | 
				
			||||||
 | 
					r = requests.get(url)
 | 
				
			||||||
 | 
					r.raise_for_status()
 | 
				
			||||||
 | 
					app = r.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					url = 'https://hosted.weblate.org/exports/stats/f-droid/f-droid-metadata/?format=json'
 | 
				
			||||||
 | 
					r = requests.get(url)
 | 
				
			||||||
 | 
					r.raise_for_status()
 | 
				
			||||||
 | 
					metadata = r.json()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#with open('f-droid-metadata.json') as fp:
 | 
				
			||||||
 | 
					#    metadata = json.load(fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app_locales = dict()
 | 
				
			||||||
 | 
					metadata_locales = dict()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					merge_locales = []
 | 
				
			||||||
 | 
					for locale in app:
 | 
				
			||||||
 | 
					    app_locales[locale['code']] = locale
 | 
				
			||||||
 | 
					for locale in metadata:
 | 
				
			||||||
 | 
					    metadata_locales[locale['code']] = locale
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for locale in sorted(app_locales.keys(), reverse=True):
 | 
				
			||||||
 | 
					    a = app_locales.get(locale)
 | 
				
			||||||
 | 
					    m = metadata_locales.get(locale)
 | 
				
			||||||
 | 
					    if a is not None and a['translated_percent'] == 100 and a['failing'] == 0 \
 | 
				
			||||||
 | 
					       and m is not None and m['translated_percent'] == 100 and m['failing'] == 0:
 | 
				
			||||||
 | 
					        print(locale)
 | 
				
			||||||
 | 
					        merge_locales.append(locale)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if not merge_locales:
 | 
				
			||||||
 | 
					    sys.exit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if 'merge_weblate' in repo.heads:
 | 
				
			||||||
 | 
					    merge_weblate = repo.heads['merge_weblate']
 | 
				
			||||||
 | 
					    repo.create_tag('previous_merge_weblate', ref=merge_weblate,
 | 
				
			||||||
 | 
					                    message=('Automatically created by %s' % __file__))
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
					    merge_weblate = repo.create_head('merge_weblate')
 | 
				
			||||||
 | 
					merge_weblate.set_commit(upstream.refs.master)
 | 
				
			||||||
 | 
					merge_weblate.checkout()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					email_pattern = re.compile(r'by (.*?) <(.*)>$')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for locale in sorted(merge_locales):
 | 
				
			||||||
 | 
					    commits = list(repo.iter_commits(
 | 
				
			||||||
 | 
					        str(weblate.refs.master) + '...' + str(upstream.refs.master),
 | 
				
			||||||
 | 
					        paths=get_paths_tuple(locale), max_count=10))
 | 
				
			||||||
 | 
					    for commit in reversed(commits):
 | 
				
			||||||
 | 
					        repo.git.cherry_pick(str(commit))
 | 
				
			||||||
 | 
					        m = email_pattern.search(commit.summary)
 | 
				
			||||||
 | 
					        if m:
 | 
				
			||||||
 | 
					            email = m.group(1) + ' <' + m.group(2) + '>'
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user