3

I have an APK-File in /storage/emulated/0/Download/app-debug.apk. I want to install this file by FileProvider because I am using Android N.

When I try to launch the intent logcat doesn't show any error but I receive following message on my phone:

There was a problem parsing the package.

What am I doing wrong?

MainActivity

public class MainActivity extends AppCompatActivity {

Button btnStartIntent;
String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();       //-> /storage/emulated/0     //-> /storage/emulated/0/Download/
String strApkToInstall = "app-debug.apk";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

/*
        --> Booooh bad way! <--
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
*/

    btnStartIntent = (Button)findViewById(R.id.btnStartIntent);
    btnStartIntent.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            installApk(MainActivity.this, strRootPathInternalStorage+"/Download/"+strApkToInstall);
        }
    });
}

public static void installApk(Context context, String apkPath) {
    if (context == null || TextUtils.isEmpty(apkPath)) {
        return;
    }


    File file = new File(apkPath);
    Intent intent = new Intent(Intent.ACTION_VIEW);

    if (Build.VERSION.SDK_INT >= 24) {
        //provider authorities
        Uri apkUri = FileProvider.getUriForFile(context, "com.spicysoftware.test.provider", file);
        //Granting Temporary Permissions to a URI
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
    } else {
        intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
    }

    context.startActivity(intent);
}

}

Manifest

<?xml version="1.0" encoding="utf-8"?>

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">


    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.spicysoftware.test.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

provider_paths.xml

    <?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="Download" path="Download" />
</paths>
MSeiz5
  • 182
  • 1
  • 9
  • 28
  • It could be that the apk you're trying to install is not made for Android Nougat. Check compileSDk, TargetSDK and minSDK for more information – Deepak kaku May 07 '18 at 15:47
  • Thanks for your fast answer. compileSDK is 24, TargetSDK 24 and minSDK24. So that shoudln't be the problem. – MSeiz5 May 07 '18 at 15:59
  • Try to install using a file explorer app on your device. – greenapps May 07 '18 at 20:11
  • https://commonsware.com/blog/2017/10/31/android-studio-3p0-flag-test-only.html – greenapps May 07 '18 at 20:14
  • Thata not a solution for my problem. If I want to use a file explorer app I would not try to do it programmatically. So I think the only acceptable way is doing it via Strictmode - VmPolicy – MSeiz5 May 07 '18 at 21:36

2 Answers2

9

For anyone who has the same problem. I had to specify the exactly folder with getExternalFilesDir();

MainActivity

public class MainActivity extends AppCompatActivity {

    Button btnStartIntentFileProvider;
    String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();         
    String strApkToInstall = "app-debug.apk";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnStartIntentFileProvider = (Button)findViewById(R.id.btnFileProvider);
        btnStartIntentFileProvider.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                File fileApkToInstall = new File(getExternalFilesDir("Download"), strApkToInstall);  

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
                    Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                    intent.setData(apkUri);
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    MainActivity.this.startActivity(intent);
                } else {
                    Uri apkUri = Uri.fromFile(fileApkToInstall);
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    MainActivity.this.startActivity(intent);
                }
            }
        });
    }
}

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.spicysoftware.test">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.spicysoftware.test.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

provider_paths (in XML Folder)

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="Download" path="Download/" />
</paths>
behrooz
  • 617
  • 9
  • 30
MSeiz5
  • 182
  • 1
  • 9
  • 28
0

After doing research and trying everything out, Here is my working code for android sdk 31:

1- created FileProvider and put it res/xml and named it "provider_paths":

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external" path="." />
</paths>

2- manifest:

    <provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>

3-In the main activity I have a function to download the apk from a server and receive the file and launch an installation intent to install the new version:

void checkUpdate()
    {
        try{
            //check if file exists delete it
            File file=new File("/sdcard/Download/app-debug.apk");
            if(file.exists())
            {
                file.delete();
                Log.d(TAG,"Update file exists, Deleting it!");
            }
            int versionCode = BuildConfig.VERSION_CODE;
            String url="https://Website.com/index.php?version="+versionCode;
            Log.d(TAG,url);
            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(20, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    .readTimeout(20, TimeUnit.SECONDS)
                    .build();
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            Response response = null;
            response = client.newCall(request).execute();
            if(response.code()!=200)
            {
                runOnUiThread(() -> Toast.makeText(getApplicationContext(),"No update found",Toast.LENGTH_LONG).show());
                return;
            }
            //downloading the file

            InputStream is = response.body().byteStream();

            BufferedInputStream input = new BufferedInputStream(is);

            String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();
            String path = strRootPathInternalStorage+"/"+Environment.DIRECTORY_DOWNLOADS+"/app-debug.apk";
            Log.d(TAG,path);
            ResponseBody body = response.body();
            long contentLength = body.contentLength();
            Log.d(TAG,""+contentLength);
            BufferedSource source = body.source();

            BufferedSink sink = null;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                sink = Okio.buffer(Okio.sink(Paths.get(path)));
            }else
            {
                Log.e(TAG,"Check this");
            }
            Buffer sinkBuffer = sink.buffer();

            long totalBytesRead = 0;
            int bufferSize = 8 * 1024;
            for (long bytesRead; (bytesRead = source.read(sinkBuffer, bufferSize)) != -1; )
            {
                sink.emit();
                totalBytesRead += bytesRead;
            }
            sink.flush();
            sink.close();
            source.close();



            //finished downloading the file  start installing
            runOnUiThread(() -> {
                try {
                    File fileApkToInstall = new File("/sdcard/Download/app-debug.apk");

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
                        Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                        intent.setData(apkUri);
                        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        MainActivity.this.startActivity(intent);
                    } else {
                        Uri apkUri = Uri.fromFile(fileApkToInstall);
                        Intent intent = new Intent(Intent.ACTION_VIEW);
                        intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        MainActivity.this.startActivity(intent);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });



        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }

4- the website update logic is to get the current version from the app and check in the server to see if it's the last version if yes than return 404 or something similar and if no than return the apk file.

Fahad Alkamli
  • 132
  • 2
  • 10