5
5
import android .content .Intent ;
6
6
import android .database .Cursor ;
7
7
import android .net .Uri ;
8
+ import android .os .Handler ;
9
+ import android .util .Log ;
10
+
11
+ import androidx .core .content .FileProvider ;
12
+
13
+ import java .io .File ;
8
14
9
15
public class AppStore {
16
+ private static final String TAG = "AppStore" ;
10
17
private static final String DOWNLOADS_DIR = "apk_downloads" ;
11
18
12
19
public void downloadAndInstallApk (Context context , String apkUrl , String fileName ) {
13
- Uri contentUri = downloadApk (context , apkUrl , fileName );
14
- installApk (context , contentUri );
20
+ Log .d (TAG , "Starting download and install process for: " + apkUrl );
21
+ Handler mainHandler = new Handler (context .getMainLooper ());
22
+
23
+ new Thread (() -> {
24
+ try {
25
+ Log .d (TAG , "Starting download..." );
26
+ Uri contentUri = downloadApk (context , apkUrl , fileName );
27
+ Log .d (TAG , "Download completed, contentUri: " + contentUri );
28
+
29
+ if (contentUri != null ) {
30
+ mainHandler .post (() -> {
31
+ Log .d (TAG , "Initiating install process" );
32
+ installApk (context , contentUri );
33
+ });
34
+ } else {
35
+ Log .e (TAG , "Download failed - contentUri is null" );
36
+ }
37
+ } catch (Exception e ) {
38
+ Log .e (TAG , "Error in download/install process" , e );
39
+ }
40
+ }).start ();
15
41
}
16
42
17
43
private Uri downloadApk (Context context , String apkUrl , String fileName ) {
18
44
DownloadManager dm = (DownloadManager ) context .getSystemService (Context .DOWNLOAD_SERVICE );
19
45
20
- DownloadManager .Request request = new DownloadManager .Request (Uri .parse (apkUrl ));
21
- request .setTitle (fileName );
22
- request .setDestinationInExternalFilesDir (context , DOWNLOADS_DIR , fileName );
46
+ try {
47
+ DownloadManager .Request request = new DownloadManager .Request (Uri .parse (apkUrl ));
48
+ request .setTitle (fileName );
49
+ request .setDestinationInExternalFilesDir (context , DOWNLOADS_DIR , fileName );
50
+ request .setAllowedOverMetered (true ); // Allow download over mobile network
51
+ request .setAllowedOverRoaming (true ); // Allow download when roaming
52
+ request .setNotificationVisibility (DownloadManager .Request .VISIBILITY_VISIBLE );
53
+
54
+ final long downloadId = dm .enqueue (request );
55
+ Log .d (TAG , "Download started with ID: " + downloadId );
56
+
57
+ int maxAttempts = 30 ; // 30 seconds timeout
58
+ int attempts = 0 ;
23
59
24
- long downloadId = dm .enqueue (request );
60
+ while (attempts < maxAttempts ) {
61
+ DownloadManager .Query query = new DownloadManager .Query ();
62
+ query .setFilterById (downloadId );
63
+ Cursor cursor = dm .query (query );
25
64
26
- DownloadManager .Query query = new DownloadManager .Query ();
27
- query .setFilterById (downloadId );
28
- Cursor cursor = dm .query (query );
65
+ if (cursor .moveToFirst ()) {
66
+ int statusIndex = cursor .getColumnIndex (DownloadManager .COLUMN_STATUS );
67
+ if (statusIndex != -1 ) {
68
+ int status = cursor .getInt (statusIndex );
69
+ int reasonIndex = cursor .getColumnIndex (DownloadManager .COLUMN_REASON );
70
+ int reason = reasonIndex != -1 ? cursor .getInt (reasonIndex ) : -1 ;
29
71
30
- Uri fileUri = null ;
31
- if (cursor != null && cursor .moveToFirst ()) {
32
- int columnIndex = cursor .getColumnIndex (DownloadManager .COLUMN_LOCAL_URI );
33
- if (columnIndex != -1 ) {
34
- String uriString = cursor .getString (columnIndex );
35
- if (uriString != null ) {
36
- fileUri = Uri .parse (uriString );
72
+ Log .d (TAG , "Download status: " + getStatusString (status ) +
73
+ ", Reason: " + getReasonString (reason ));
74
+
75
+ if (status == DownloadManager .STATUS_SUCCESSFUL ) {
76
+ int uriIndex = cursor .getColumnIndex (DownloadManager .COLUMN_LOCAL_URI );
77
+ if (uriIndex != -1 ) {
78
+ String uriString = cursor .getString (uriIndex );
79
+ cursor .close ();
80
+ return Uri .parse (uriString );
81
+ }
82
+ } else if (status == DownloadManager .STATUS_FAILED ) {
83
+ cursor .close ();
84
+ throw new RuntimeException ("Download failed with reason: " + getReasonString (reason ));
85
+ }
86
+ }
87
+ }
88
+ cursor .close ();
89
+
90
+ attempts ++;
91
+ try {
92
+ Thread .sleep (1000 );
93
+ } catch (InterruptedException e ) {
94
+ Thread .currentThread ().interrupt ();
95
+ throw new RuntimeException ("Download interrupted" , e );
37
96
}
38
97
}
39
- cursor .close ();
98
+
99
+ // If we get here, download timed out
100
+ dm .remove (downloadId );
101
+ throw new RuntimeException ("Download timed out after " + maxAttempts + " seconds" );
102
+
103
+ } catch (Exception e ) {
104
+ Log .e (TAG , "Error in downloadApk" , e );
105
+ throw e ;
106
+ }
107
+ }
108
+ private String getStatusString (int status ) {
109
+ switch (status ) {
110
+ case DownloadManager .STATUS_FAILED :
111
+ return "STATUS_FAILED" ;
112
+ case DownloadManager .STATUS_PAUSED :
113
+ return "STATUS_PAUSED" ;
114
+ case DownloadManager .STATUS_PENDING :
115
+ return "STATUS_PENDING" ;
116
+ case DownloadManager .STATUS_RUNNING :
117
+ return "STATUS_RUNNING" ;
118
+ case DownloadManager .STATUS_SUCCESSFUL :
119
+ return "STATUS_SUCCESSFUL" ;
120
+ default :
121
+ return "STATUS_UNKNOWN_" + status ;
40
122
}
123
+ }
41
124
42
- return fileUri ;
125
+ private String getReasonString (int reason ) {
126
+ switch (reason ) {
127
+ case DownloadManager .ERROR_CANNOT_RESUME : return "ERROR_CANNOT_RESUME" ;
128
+ case DownloadManager .ERROR_DEVICE_NOT_FOUND : return "ERROR_DEVICE_NOT_FOUND" ;
129
+ case DownloadManager .ERROR_FILE_ALREADY_EXISTS : return "ERROR_FILE_ALREADY_EXISTS" ;
130
+ case DownloadManager .ERROR_FILE_ERROR : return "ERROR_FILE_ERROR" ;
131
+ case DownloadManager .ERROR_HTTP_DATA_ERROR : return "ERROR_HTTP_DATA_ERROR" ;
132
+ case DownloadManager .ERROR_INSUFFICIENT_SPACE : return "ERROR_INSUFFICIENT_SPACE" ;
133
+ case DownloadManager .ERROR_TOO_MANY_REDIRECTS : return "ERROR_TOO_MANY_REDIRECTS" ;
134
+ case DownloadManager .ERROR_UNHANDLED_HTTP_CODE : return "ERROR_UNHANDLED_HTTP_CODE" ;
135
+ case DownloadManager .ERROR_UNKNOWN : return "ERROR_UNKNOWN" ;
136
+ case DownloadManager .PAUSED_QUEUED_FOR_WIFI : return "PAUSED_QUEUED_FOR_WIFI" ;
137
+ case DownloadManager .PAUSED_UNKNOWN : return "PAUSED_UNKNOWN" ;
138
+ case DownloadManager .PAUSED_WAITING_FOR_NETWORK : return "PAUSED_WAITING_FOR_NETWORK" ;
139
+ case DownloadManager .PAUSED_WAITING_TO_RETRY : return "PAUSED_WAITING_TO_RETRY" ;
140
+ default : return "UNKNOWN_REASON_" + reason ;
141
+ }
43
142
}
44
143
45
- private void installApk (Context context , Uri contentUri ) {
46
- Intent intent = new Intent ("com.luddite.app.store.INSTALL_PACKAGE" );
47
- intent .setDataAndType (contentUri , "application/vnd.android.package-archive" );
48
- intent .addFlags (Intent .FLAG_GRANT_READ_URI_PERMISSION );
49
- context .startActivity (intent );
144
+ private void installApk (Context context , Uri fileUri ) {
145
+ try {
146
+ File file = new File (fileUri .getPath ());
147
+ Uri contentUri = FileProvider .getUriForFile (context ,
148
+ context .getPackageName () + ".provider" ,
149
+ file );
150
+
151
+ Intent intent = new Intent ("com.luddite.app.store.INSTALL_PACKAGE" );
152
+ intent .setDataAndType (contentUri , "application/vnd.android.package-archive" );
153
+ intent .addFlags (Intent .FLAG_GRANT_READ_URI_PERMISSION );
154
+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
155
+ context .startActivity (intent );
156
+ } catch (Exception e ) {
157
+ Log .e (TAG , "Error in installApk" , e );
158
+ }
50
159
}
51
160
}
0 commit comments