diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f5d2d622..b4ae7cd49 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -316,12 +316,18 @@
+
toArrayList() {
+ ArrayList out = new ArrayList<>();
+ for (String element : this) {
+ out.add(element);
+ }
+ return out;
+ }
+
+ public String[] toArray() {
+ ArrayList list = toArrayList();
+ return list.toArray(new String[list.size()]);
+ }
+
public boolean contains(String v) {
for (final String s : this) {
if (s.equals(v)) {
diff --git a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java
index f1629c657..e1395f92d 100644
--- a/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java
+++ b/app/src/main/java/org/fdroid/fdroid/data/ApkProvider.java
@@ -107,8 +107,12 @@ public class ApkProvider extends FDroidProvider {
}
public static Apk find(Context context, String packageName, int versionCode, String[] projection) {
- ContentResolver resolver = context.getContentResolver();
final Uri uri = getContentUri(packageName, versionCode);
+ return find(context, uri, projection);
+ }
+
+ public static Apk find(Context context, Uri uri, String[] projection) {
+ ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, projection, null, null, null);
Apk apk = null;
if (cursor != null) {
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
index d43f6d040..169c2a07c 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/DefaultInstaller.java
@@ -85,4 +85,9 @@ public class DefaultInstaller extends Installer {
sendBroadcastUninstall(packageName,
Installer.ACTION_UNINSTALL_USER_INTERACTION, uninstallPendingIntent);
}
+
+ @Override
+ protected boolean isUnattended() {
+ return false;
+ }
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
index c778a6897..8782085bf 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/ExtensionInstaller.java
@@ -96,4 +96,9 @@ public class ExtensionInstaller extends Installer {
// don't use broadcasts for the rest of this special installer
sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_COMPLETE);
}
+
+ @Override
+ protected boolean isUnattended() {
+ return false;
+ }
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
index 3663ecd5c..1a8e1de9a 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java
@@ -37,6 +37,8 @@ import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.SanitizedFile;
import org.fdroid.fdroid.privileged.views.AppDiff;
import org.fdroid.fdroid.privileged.views.AppSecurityPermissions;
+import org.fdroid.fdroid.privileged.views.InstallConfirmActivity;
+import org.fdroid.fdroid.privileged.views.UninstallDialogActivity;
import java.io.File;
import java.io.IOException;
@@ -153,48 +155,55 @@ public abstract class Installer {
return Uri.fromFile(sanitizedApkFile);
}
- public PendingIntent getPermissionScreen(Apk apk) {
- // old code:
-// Uri packageUri = Uri.fromFile(apkFile);
-// int count = newPermissionCount(packageUri);
-// if (count < 0) {
-// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
-// InstallerCallback.ERROR_CODE_CANNOT_PARSE);
+ public Intent getPermissionScreen(Apk apk) {
+ if (!isUnattended()) {
+ return null;
+ }
-// install_error_cannot_parse
+ int count = newPermissionCount(apk);
+ if (count > 0) {
+ Uri uri = ApkProvider.getContentUri(apk);
+ Intent intent = new Intent(mContext, InstallConfirmActivity.class);
+ intent.setData(uri);
-// return;
-// }
-// if (count > 0) {
-// Intent intent = new Intent(mContext, InstallConfirmActivity.class);
-// intent.setData(packageUri);
-// mActivity.startActivityForResult(intent, REQUEST_CONFIRM_PERMS);
-// } else {
-// try {
-// doInstallPackageInternal(packageUri);
-// } catch (InstallFailedException e) {
-// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
-// InstallerCallback.ERROR_CODE_OTHER);
-// }
-// }
- return null;
+ return intent;
+ } else {
+ // no permission screen needed!
+ return null;
+ }
}
+ private int newPermissionCount(Apk apk) {
+ // TODO: requires targetSdk in Apk class/database
+// boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
+// >= Build.VERSION_CODES.M;
+// if (supportsRuntimePermissions) {
+// return 0;
+// }
- private int newPermissionCount(Uri packageUri) {
- AppDiff appDiff = new AppDiff(mContext.getPackageManager(), packageUri);
+ AppDiff appDiff = new AppDiff(mContext.getPackageManager(), apk);
if (appDiff.mPkgInfo == null) {
// could not get diff because we couldn't parse the package
- return -1;
+ throw new RuntimeException("cannot parse!");
}
AppSecurityPermissions perms = new AppSecurityPermissions(mContext, appDiff.mPkgInfo);
if (appDiff.mInstalledAppInfo != null) {
// update to an existing app
return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW);
}
- // default: even if there aren't any permissions, we want to make the
- // user always confirm installing new apps
- return 1;
+ // new app install
+ return perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
+ }
+
+ public Intent getUninstallScreen(String packageName) {
+ if (!isUnattended()) {
+ return null;
+ }
+
+ Intent intent = new Intent(mContext, UninstallDialogActivity.class);
+ intent.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+
+ return intent;
}
/**
@@ -287,4 +296,6 @@ public abstract class Installer {
protected abstract void uninstallPackage(String packageName);
+ protected abstract boolean isUnattended();
+
}
diff --git a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
index 8c3dfcdb4..420adc0b5 100644
--- a/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
+++ b/app/src/main/java/org/fdroid/fdroid/installer/PrivilegedInstaller.java
@@ -238,7 +238,7 @@ public class PrivilegedInstaller extends Installer {
private static final HashMap sUninstallReturnCodes;
static {
- // Descriptions extrgacted from the source code comments in AOSP
+ // Descriptions extracted from the source code comments in AOSP
sUninstallReturnCodes = new HashMap<>();
sUninstallReturnCodes.put(DELETE_SUCCEEDED,
"Success");
@@ -324,6 +324,7 @@ public class PrivilegedInstaller extends Installer {
@Override
protected void installPackage(final Uri uri, final Uri originatingUri, String packageName) {
+ sendBroadcastInstall(uri, originatingUri, Installer.ACTION_INSTALL_STARTED);
final Uri sanitizedUri;
try {
@@ -374,6 +375,8 @@ public class PrivilegedInstaller extends Installer {
@Override
protected void uninstallPackage(final String packageName) {
+ sendBroadcastUninstall(packageName, Installer.ACTION_UNINSTALL_STARTED);
+
ApplicationInfo appInfo;
try {
//noinspection WrongConstant (lint is actually wrong here!)
@@ -466,33 +469,9 @@ public class PrivilegedInstaller extends Installer {
Context.BIND_AUTO_CREATE);
}
-// @Override
-// public boolean handleOnActivityResult(int requestCode, int resultCode, Intent data) {
-// switch (requestCode) {
-// case REQUEST_CONFIRM_PERMS:
-// if (resultCode == Activity.RESULT_OK) {
-// final Uri packageUri = data.getData();
-// try {
-// doInstallPackageInternal(packageUri);
-// } catch (InstallFailedException e) {
-// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
-// InstallerCallback.ERROR_CODE_OTHER);
-// }
-// } else if (resultCode == InstallConfirmActivity.RESULT_CANNOT_PARSE) {
-// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
-// InstallerCallback.ERROR_CODE_CANNOT_PARSE);
-
-// install_error_cannot_parse
-
-// } else { // Activity.RESULT_CANCELED
-// mCallback.onError(InstallerCallback.OPERATION_INSTALL,
-// InstallerCallback.ERROR_CODE_CANCELED);
-// }
-// return true;
-// default:
-// return false;
-// }
-// }
-
+ @Override
+ protected boolean isUnattended() {
+ return true;
+ }
}
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
index 096f0290b..46829a162 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java
@@ -18,11 +18,18 @@
package org.fdroid.fdroid.privileged.views;
+import android.annotation.TargetApi;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Build;
+import org.fdroid.fdroid.data.Apk;
+
+import java.util.ArrayList;
+
+@TargetApi(Build.VERSION_CODES.M)
public class AppDiff {
private final PackageManager mPm;
@@ -30,6 +37,29 @@ public class AppDiff {
public ApplicationInfo mInstalledAppInfo;
+ /**
+ * Constructor based on F-Droids Apk object
+ */
+ public AppDiff(PackageManager mPm, Apk apk) {
+ this.mPm = mPm;
+
+ if (apk.permissions == null) {
+ throw new RuntimeException("apk.permissions is null");
+ }
+ mPkgInfo = new PackageInfo();
+ mPkgInfo.packageName = apk.packageName;
+ mPkgInfo.applicationInfo = new ApplicationInfo();
+
+ // TODO: duplicate code with Permission.fdroidToAndroid
+ ArrayList permissionsFixed = new ArrayList<>();
+ for (String perm : apk.permissions.toArrayList()) {
+ permissionsFixed.add("android.permission." + perm);
+ }
+ mPkgInfo.requestedPermissions = permissionsFixed.toArray(new String[permissionsFixed.size()]);
+
+ init();
+ }
+
public AppDiff(PackageManager mPm, Uri mPackageURI) {
this.mPm = mPm;
@@ -55,7 +85,7 @@ public class AppDiff {
String pkgName = mPkgInfo.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 = mPm.canonicalToCurrentPackageNames(new String[] {pkgName});
+ final String[] oldName = mPm.canonicalToCurrentPackageNames(new String[]{pkgName});
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java
index b48108682..75981bbc5 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppSecurityPermissions.java
@@ -235,8 +235,7 @@ public class AppSecurityPermissions {
try {
installedPkgInfo = mPm.getPackageInfo(info.packageName,
PackageManager.GET_PERMISSIONS);
- } catch (NameNotFoundException e) {
- throw new RuntimeException("NameNotFoundException during GET_PERMISSIONS!");
+ } catch (NameNotFoundException ignored) {
}
extractPerms(info, permSet, installedPkgInfo);
}
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
index ceddb8f43..3d983633b 100644
--- a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java
@@ -18,16 +18,16 @@
package org.fdroid.fdroid.privileged.views;
-import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
@@ -38,15 +38,23 @@ import android.widget.ImageView;
import android.widget.TabHost;
import android.widget.TextView;
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import com.nostra13.universalimageloader.core.assist.ImageScaleType;
+
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.data.Apk;
+import org.fdroid.fdroid.data.ApkProvider;
+import org.fdroid.fdroid.data.App;
+import org.fdroid.fdroid.data.AppProvider;
/**
* NOTES:
* Parts are based on AOSP src/com/android/packageinstaller/PackageInstallerActivity.java
* latest included commit: c23d802958158d522e7350321ad9ac6d43013883
*/
-public class InstallConfirmActivity extends Activity implements OnCancelListener, OnClickListener {
+public class InstallConfirmActivity extends FragmentActivity implements OnCancelListener, OnClickListener {
public static final int RESULT_CANNOT_PARSE = RESULT_FIRST_USER + 1;
@@ -67,16 +75,27 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
private static final String TAB_ID_ALL = "all";
private static final String TAB_ID_NEW = "new";
+ private App mApp;
+
+ private final DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder()
+ .cacheInMemory(true)
+ .cacheOnDisk(true)
+ .imageScaleType(ImageScaleType.NONE)
+ .showImageOnLoading(R.drawable.ic_repo_app_default)
+ .showImageForEmptyUri(R.drawable.ic_repo_app_default)
+ .bitmapConfig(Bitmap.Config.RGB_565)
+ .build();
+
private void startInstallConfirm() {
-
- final Drawable appIcon = mAppDiff.mPkgInfo.applicationInfo.loadIcon(mPm);
- final String appLabel = (String) mAppDiff.mPkgInfo.applicationInfo.loadLabel(mPm);
-
View appSnippet = findViewById(R.id.app_snippet);
- ((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(appIcon);
- ((TextView) appSnippet.findViewById(R.id.app_name)).setText(appLabel);
-
+ TextView appName = (TextView) appSnippet.findViewById(R.id.app_name);
+ ImageView appIcon = (ImageView) appSnippet.findViewById(R.id.app_icon);
TabHost tabHost = (TabHost) findViewById(android.R.id.tabhost);
+
+ appName.setText(mApp.name);
+ ImageLoader.getInstance().displayImage(mApp.iconUrlLarge, appIcon,
+ displayImageOptions);
+
tabHost.setup();
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
@@ -136,7 +155,7 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
: R.string.install_confirm_update_no_perms;
} else {
// This is a new application with no permissions.
- msg = R.string.install_confirm_no_perms;
+ throw new RuntimeException("no permissions requested. This screen should not appear!");
}
tabHost.setVisibility(View.GONE);
findViewById(R.id.filler).setVisibility(View.VISIBLE);
@@ -171,20 +190,28 @@ public class InstallConfirmActivity extends Activity implements OnCancelListener
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- ((FDroidApp) getApplication()).applyTheme(this);
+ ((FDroidApp) getApplication()).applyDialogTheme(this);
mPm = getPackageManager();
intent = getIntent();
- Uri packageURI = intent.getData();
+ Uri uri = intent.getData();
+ Apk apk = ApkProvider.Helper.find(this, uri, ApkProvider.DataColumns.ALL);
+ mApp = AppProvider.Helper.findByPackageName(getContentResolver(), apk.packageName);
- mAppDiff = new AppDiff(mPm, packageURI);
+ mAppDiff = new AppDiff(mPm, apk);
if (mAppDiff.mPkgInfo == null) {
setResult(RESULT_CANNOT_PARSE, intent);
finish();
}
setContentView(R.layout.install_start);
+
+ // increase dialog to full width for now
+ // TODO: create a better design and minimum width for tablets
+ getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java
new file mode 100644
index 000000000..f0d2bd04c
--- /dev/null
+++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/UninstallDialogActivity.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 Dominik Schürmann
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+package org.fdroid.fdroid.privileged.views;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AlertDialog;
+import android.view.ContextThemeWrapper;
+
+import org.fdroid.fdroid.FDroidApp;
+import org.fdroid.fdroid.R;
+import org.fdroid.fdroid.installer.Installer;
+
+public class UninstallDialogActivity extends FragmentActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ final String packageName = intent.getStringExtra(Installer.EXTRA_PACKAGE_NAME);
+
+ PackageManager pm = getPackageManager();
+
+ ApplicationInfo appInfo;
+ try {
+ //noinspection WrongConstant (lint is actually wrong here!)
+ appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException("Failed to get ApplicationInfo for uninstalling");
+ }
+
+ final boolean isSystem = (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ final boolean isUpdate = (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+
+ if (isSystem && !isUpdate) {
+ // Cannot remove system apps unless we're uninstalling updates
+ throw new RuntimeException("Cannot remove system apps unless we're uninstalling updates");
+ }
+
+ int messageId;
+ if (isUpdate) {
+ messageId = R.string.uninstall_update_confirm;
+ } else {
+ messageId = R.string.uninstall_confirm;
+ }
+
+ // hack to get theme applied (which is not automatically applied due to activity's Theme.NoDisplay
+ ContextThemeWrapper theme = new ContextThemeWrapper(this, FDroidApp.getCurThemeResId());
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(theme);
+ builder.setTitle(appInfo.loadLabel(pm));
+ builder.setIcon(appInfo.loadIcon(pm));
+ builder.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent data = new Intent();
+ data.putExtra(Installer.EXTRA_PACKAGE_NAME, packageName);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+ });
+ builder.setOnCancelListener(
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+ });
+ builder.setMessage(messageId);
+ builder.create().show();
+ }
+}
diff --git a/app/src/main/res/layout-v11/install_confirm.xml b/app/src/main/res/layout-v11/install_confirm.xml
index 7676b2b75..e0d978ff1 100644
--- a/app/src/main/res/layout-v11/install_confirm.xml
+++ b/app/src/main/res/layout-v11/install_confirm.xml
@@ -21,37 +21,35 @@
user before it is installed.
-->
-
+
+ android:id="@+id/install_confirm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="4dip"
+ android:text="@string/install_confirm"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:background="?android:attr/dividerHorizontal"
+ android:visibility="gone" />
-
+ android:visibility="gone">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
-
+
+
+
+ android:layout_gravity="center"
+ android:orientation="horizontal" />
@@ -85,64 +87,68 @@
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_weight="0"/>
+ android:layout_weight="0" />
+ android:layout_weight="1" />
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerHorizontal"
+ android:orientation="vertical"
+ android:showDividers="beginning">
+
+
+ android:visibility="gone" />
-
+
-
+
-
-
-
+
diff --git a/app/src/main/res/layout/install_app_details.xml b/app/src/main/res/layout/install_app_details.xml
index e129dbb36..5fa67e8a9 100644
--- a/app/src/main/res/layout/install_app_details.xml
+++ b/app/src/main/res/layout/install_app_details.xml
@@ -1,5 +1,4 @@
-
-
-
-
+
-
+
+
-
+
+
-
-
-
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:text="App Name" />
-
+
diff --git a/app/src/main/res/layout/install_confirm.xml b/app/src/main/res/layout/install_confirm.xml
index fd2a97271..b7e81e429 100644
--- a/app/src/main/res/layout/install_confirm.xml
+++ b/app/src/main/res/layout/install_confirm.xml
@@ -1,5 +1,4 @@
-
-
-
+
+ android:id="@+id/install_confirm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="4dip"
+ android:text="@string/install_confirm"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:visibility="gone" />
-
+ android:visibility="gone">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
-
+
+
+
+ android:layout_gravity="center"
+ android:orientation="horizontal" />
@@ -84,63 +85,65 @@
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
- android:layout_weight="0"/>
+ android:layout_weight="0" />
+ android:layout_weight="1" />
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:attr/dividerHorizontal"
+ android:orientation="vertical"
+ android:showDividers="beginning">
+
+
+ android:visibility="gone" />
-
+
-
+
-
-
-
+
diff --git a/app/src/main/res/layout/install_start.xml b/app/src/main/res/layout/install_start.xml
index 0fb199f8a..189936bab 100644
--- a/app/src/main/res/layout/install_start.xml
+++ b/app/src/main/res/layout/install_start.xml
@@ -1,5 +1,4 @@
-
-
-
+ android:layout_height="wrap_content">
+ android:layout_height="wrap_content" />
+ android:layout_below="@id/app_snippet" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a9b4acd8e..26f985b7c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -268,7 +268,6 @@
Root access denied
Either your Android device is not rooted or you have denied root access for F-Droid.
Failed to install due to an unknown error
- An error occurred while parsing the package
Failed to uninstall due to an unknown error
F-Droid Privileged Extension is not available
This option is only available when F-Droid Privileged Extension is installed.
@@ -339,10 +338,7 @@
Tap to install %s
Tap to update %s
- Do you want to install this application?
- It will get access to:
- Do you want to install this application?
- It does not require any special access.
+ needs access to
Do you want to install an update
to this existing application? Your existing data will not
be lost. The updated application will get access to:
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 254dbe092..5a9c2e85c 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -48,6 +48,20 @@
- @color/fdroid_green
+
+
+
+