├── ChromiumAutoUpdater ├── .gitignore ├── .idea │ ├── caches │ │ ├── build_file_checksums.ser │ │ └── gradle_models.ser │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── modules.xml │ └── runConfigurations.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── dosse │ │ │ └── chromiumautoupdater │ │ │ ├── AppCompatPreferenceActivity.java │ │ │ ├── ChromiumAlreadyInstalledActivity.java │ │ │ ├── ChromiumUpdater.java │ │ │ ├── EnableNonMarketAppsActivity.java │ │ │ ├── IntroActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── Settings2Activity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── ShowErrorActivity.java │ │ │ ├── Starter.java │ │ │ └── Utils.java │ │ └── res │ │ ├── drawable │ │ ├── introbk.xml │ │ ├── introbk2.xml │ │ ├── introbk3.xml │ │ ├── introbk4.xml │ │ └── notification.png │ │ ├── layout-v23 │ │ └── activity_show_error.xml │ │ ├── layout │ │ ├── activity_chromium_already_installed.xml │ │ ├── activity_enable_non_market_apps.xml │ │ ├── activity_intro.xml │ │ ├── activity_main.xml │ │ ├── activity_show_error.xml │ │ ├── fragment_intro.xml │ │ ├── fragment_intro2.xml │ │ ├── fragment_intro3.xml │ │ └── fragment_intro4.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-nb │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── pref_advanced.xml │ │ └── pref_general.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md ├── icon_new.png ├── icon_new.svg └── notification.png /ChromiumAutoUpdater/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adolfintel/chromiumUpdater/7ef9077c8d23307b43290652fac3933431bc1a62/ChromiumAutoUpdater/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/caches/gradle_models.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adolfintel/chromiumUpdater/7ef9077c8d23307b43290652fac3933431bc1a62/ChromiumAutoUpdater/.idea/caches/gradle_models.ser -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '28.0.3' 6 | defaultConfig { 7 | applicationId "com.dosse.chromiumautoupdater" 8 | minSdkVersion 21 9 | targetSdkVersion 26 10 | versionCode 26 11 | versionName '1.8.4' 12 | vectorDrawables.useSupportLibrary = true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | productFlavors { 21 | } 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(include: ['*.jar'], dir: 'libs') 26 | implementation 'com.android.support:support-v4:26.1.0' 27 | implementation 'com.android.support:support-vector-drawable:26.1.0' 28 | implementation 'com.android.support:design:26.1.0' 29 | implementation 'com.android.support:appcompat-v7:26.1.0' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | } 32 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Federico\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 46 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/AppCompatPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Bundle; 5 | import android.preference.PreferenceActivity; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.ActionBar; 9 | import android.support.v7.app.AppCompatDelegate; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.MenuInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | /** 16 | * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls 17 | * to be used with AppCompat. 18 | */ 19 | public abstract class AppCompatPreferenceActivity extends PreferenceActivity { 20 | 21 | private AppCompatDelegate mDelegate; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | getDelegate().installViewFactory(); 26 | getDelegate().onCreate(savedInstanceState); 27 | super.onCreate(savedInstanceState); 28 | } 29 | 30 | @Override 31 | protected void onPostCreate(Bundle savedInstanceState) { 32 | super.onPostCreate(savedInstanceState); 33 | getDelegate().onPostCreate(savedInstanceState); 34 | } 35 | 36 | public ActionBar getSupportActionBar() { 37 | return getDelegate().getSupportActionBar(); 38 | } 39 | 40 | public void setSupportActionBar(@Nullable Toolbar toolbar) { 41 | getDelegate().setSupportActionBar(toolbar); 42 | } 43 | 44 | @Override 45 | public MenuInflater getMenuInflater() { 46 | return getDelegate().getMenuInflater(); 47 | } 48 | 49 | @Override 50 | public void setContentView(@LayoutRes int layoutResID) { 51 | getDelegate().setContentView(layoutResID); 52 | } 53 | 54 | @Override 55 | public void setContentView(View view) { 56 | getDelegate().setContentView(view); 57 | } 58 | 59 | @Override 60 | public void setContentView(View view, ViewGroup.LayoutParams params) { 61 | getDelegate().setContentView(view, params); 62 | } 63 | 64 | @Override 65 | public void addContentView(View view, ViewGroup.LayoutParams params) { 66 | getDelegate().addContentView(view, params); 67 | } 68 | 69 | @Override 70 | protected void onPostResume() { 71 | super.onPostResume(); 72 | getDelegate().onPostResume(); 73 | } 74 | 75 | @Override 76 | protected void onTitleChanged(CharSequence title, int color) { 77 | super.onTitleChanged(title, color); 78 | getDelegate().setTitle(title); 79 | } 80 | 81 | @Override 82 | public void onConfigurationChanged(Configuration newConfig) { 83 | super.onConfigurationChanged(newConfig); 84 | getDelegate().onConfigurationChanged(newConfig); 85 | } 86 | 87 | @Override 88 | protected void onStop() { 89 | super.onStop(); 90 | getDelegate().onStop(); 91 | } 92 | 93 | @Override 94 | protected void onDestroy() { 95 | super.onDestroy(); 96 | getDelegate().onDestroy(); 97 | } 98 | 99 | public void invalidateOptionsMenu() { 100 | getDelegate().invalidateOptionsMenu(); 101 | } 102 | 103 | private AppCompatDelegate getDelegate() { 104 | if (mDelegate == null) { 105 | mDelegate = AppCompatDelegate.create(this, null); 106 | } 107 | return mDelegate; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/ChromiumAlreadyInstalledActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.provider.Settings; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Button; 10 | 11 | public class ChromiumAlreadyInstalledActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_chromium_already_installed); 17 | //event listener for uninstall button 18 | Button b=(Button)(findViewById(R.id.button3)); 19 | b.setOnClickListener(new View.OnClickListener() { 20 | @Override 21 | public void onClick(View view) { 22 | Intent intent=new Intent(); 23 | intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 24 | Uri uri = Uri.fromParts("package", "org.chromium.chrome", null); 25 | intent.setData(uri); 26 | startActivity(intent); 27 | finish(); 28 | } 29 | }); 30 | //event listener for ignore button 31 | b=(Button)(findViewById(R.id.button4)); 32 | b.setOnClickListener(new View.OnClickListener() { 33 | @Override 34 | public void onClick(View view) { 35 | Intent intent=new Intent(); 36 | Intent i = new Intent(ChromiumAlreadyInstalledActivity.this, MainActivity.class); 37 | startActivity(i); 38 | finish(); 39 | } 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/ChromiumUpdater.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.IntentService; 5 | import android.app.NotificationChannel; 6 | import android.app.NotificationManager; 7 | import android.app.PendingIntent; 8 | import android.app.Service; 9 | import android.content.BroadcastReceiver; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.content.IntentFilter; 13 | import android.content.SharedPreferences; 14 | import android.net.Uri; 15 | import android.os.Build; 16 | import android.os.Environment; 17 | import android.os.IBinder; 18 | import android.os.StatFs; 19 | import android.os.StrictMode; 20 | import android.support.v4.app.NotificationCompat; 21 | import android.util.Log; 22 | import android.widget.Toast; 23 | 24 | import static com.dosse.chromiumautoupdater.Utils.log; 25 | 26 | import java.io.BufferedInputStream; 27 | import java.io.BufferedReader; 28 | import java.io.BufferedWriter; 29 | import java.io.DataInputStream; 30 | import java.io.DataOutputStream; 31 | import java.io.EOFException; 32 | import java.io.File; 33 | import java.io.FileInputStream; 34 | import java.io.FileNotFoundException; 35 | import java.io.FileOutputStream; 36 | import java.io.InputStream; 37 | import java.io.InputStreamReader; 38 | import java.io.OutputStream; 39 | import java.io.OutputStreamWriter; 40 | import java.net.URL; 41 | import java.net.URLConnection; 42 | import java.util.zip.ZipEntry; 43 | import java.util.zip.ZipInputStream; 44 | 45 | /** 46 | * The actual Updater Service. 47 | * 48 | * When the service is started, it creates a new thread which checks periodically for updates. 49 | * Every 10 seconds, it checks the timestamp and if it's been long enough since the last update was installed (or an update was forced), it will download and install the latest APK. 50 | * 51 | */ 52 | 53 | public class ChromiumUpdater extends Service { 54 | private static final String TAG = "Chromium Updater Svc"; 55 | 56 | private static UpdateThread t=null; 57 | private static boolean forcedUpdateRequested=false; 58 | private static boolean busy=false; //if for obscure reasons the service is started multiple times, this is used to avoid overlapping 59 | public static boolean isBusy(){return busy;} 60 | 61 | public static boolean cancelRequested=false; //set to true by Starter class when a CANCEL_ACTION event is received (cancel button on notification) 62 | public static NotificationManager notifMan=null; //just for convenience, we make this public so that the Starter class can cancel notifications directly without waiting for this thread 63 | 64 | private static final long MIN_FREE_SPACE=300*1024*1024; 65 | 66 | /** 67 | * The actual thread which checks, downloads and installs updates 68 | */ 69 | private class UpdateThread extends Thread{ 70 | public void run(){ 71 | long lastUpdate=0; 72 | //read last update timestamp from file 73 | try{ 74 | DataInputStream fis=new DataInputStream(getApplicationContext().openFileInput("lastUpdate")); 75 | lastUpdate=fis.readLong(); 76 | fis.close(); 77 | }catch(Throwable e){} 78 | for(;;) { 79 | if(!busy){ 80 | log("THREAD ALIVE"); 81 | cancelRequested=false; 82 | SharedPreferences prefs=getSharedPreferences("chromiumUpdater",MODE_PRIVATE); 83 | //how often do we need to download updates? 84 | long updateEvery=Integer.parseInt(prefs.getString("updateEvery","7"))*86400000L; //days -> milliseconds 85 | if(lastUpdate>Utils.getTimestamp()){log("lastUpdate is in the future. Discarding."); lastUpdate=0;} //if the timestamp was invalid, lastUpdate will be 0 and chromium will be downloaded asap 86 | log("Timestamp: "+Utils.getTimestamp()+" Last update: "+lastUpdate+" Next update: "+(lastUpdate+updateEvery)); 87 | if(forcedUpdateRequested||(prefs.getBoolean("autoSwitch",true)&&Utils.getTimestamp()-lastUpdate>=updateEvery)){ //time to update 88 | if(forcedUpdateRequested){ 89 | log("Forced update requested"); 90 | } 91 | log("Updating Chromium"); 92 | lastUpdate=downloadUpdate(); //downloadUpdate will download and install the latest build and return either the current timestamp (success) or 0 (error, try again asap) 93 | //save timestamp (or 0) to file 94 | try{ 95 | DataOutputStream fos=new DataOutputStream(getApplicationContext().openFileOutput("lastUpdate", Context.MODE_PRIVATE)); 96 | fos.writeLong(lastUpdate); 97 | fos.close(); 98 | }catch(Throwable e){} 99 | if(forcedUpdateRequested){ 100 | forcedUpdateRequested=false; 101 | } 102 | }else log("No update necessary"); 103 | }else log("Updater is busy, skipping tick"); 104 | //wait 10 seconds 105 | try{sleep(10000);}catch(Throwable e){} 106 | } 107 | } 108 | 109 | /** 110 | * ignorable exceptions will not be shown even when notify errors is enabled 111 | */ 112 | private class IgnorableException extends Exception { 113 | 114 | public IgnorableException(String message) { 115 | super(message); 116 | } 117 | } 118 | 119 | /** 120 | * Downloads and installs the latest version of chromium from https://commondatastorage.googleapis.com/chromium-browser-snapshots/Android 121 | * @return timestamp (success) or 0 (error) 122 | */ 123 | private long downloadUpdate(){ 124 | long ret=Utils.getTimestamp(); 125 | synchronized(ChromiumUpdater.this){ 126 | busy=true; 127 | NotificationManager mNotifyManager=null; 128 | try { 129 | //can we actually update chromium? 130 | if(!Utils.isRooted()&&!Utils.thirdPartyAppsAllowed(getContentResolver())){ 131 | throw new Exception("No root and no third party apps allowed"); 132 | } 133 | if(!Utils.canWriteToSdcard(ChromiumUpdater.this)){ 134 | throw new Exception("Cannot write to sdcard"); 135 | } 136 | if(!Utils.isConnected(getApplicationContext())){ 137 | throw new IgnorableException("No Internet"); 138 | } 139 | SharedPreferences prefs=getSharedPreferences("chromiumUpdater",MODE_PRIVATE); 140 | if(!forcedUpdateRequested){ 141 | if(prefs.getBoolean("noMobileConnections",false)&&Utils.isMobileConnection(getApplicationContext())){ 142 | throw new IgnorableException("Avoiding mobile connection"); 143 | } 144 | } 145 | //ok, we can do it 146 | //intent for cancel button on notification 147 | Intent cancelReceive =new Intent(); 148 | cancelReceive.setAction("CANCEL_ACTION"); 149 | PendingIntent cancelIntent=PendingIntent.getBroadcast(getApplicationContext(),12345,cancelReceive,PendingIntent.FLAG_IMMUTABLE); 150 | //create update notification with indeterminate progressbar 151 | mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 152 | notifMan=mNotifyManager; 153 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //on oreo and newer, create a notification channel 154 | NotificationChannel channel = new NotificationChannel(TAG, TAG, NotificationManager.IMPORTANCE_LOW); 155 | channel.enableVibration(false); 156 | channel.enableLights(false); 157 | channel.setShowBadge(true); 158 | notifMan.createNotificationChannel(channel); 159 | } 160 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ChromiumUpdater.this,TAG); 161 | mBuilder.setContentTitle(getString(R.string.notification_title)).setSmallIcon(R.drawable.notification).setContentText(getString(R.string.notification_starting)).setOngoing(true).setShowWhen(false); 162 | mBuilder.setProgress(100, 0, true); 163 | mBuilder.addAction(android.support.design.R.drawable.navigation_empty_icon,getString(R.string.notification_cancel),cancelIntent); //add cancel button 164 | mNotifyManager.notify(1, mBuilder.build()); 165 | File sdcard=Environment.getExternalStorageDirectory(); 166 | if(cancelRequested){ 167 | throw new IgnorableException("Cancelled by user"); 168 | } 169 | //get id of latest build 170 | String latestVer=null; 171 | URL u; 172 | URLConnection c; 173 | if(getSharedPreferences("chromiumUpdater",MODE_PRIVATE).getString("channel",getString(R.string.channelValue_latest)).equalsIgnoreCase(getString(R.string.channelValue_stable))){ 174 | u=new URL("https://omahaproxy.appspot.com/all?channel=stable&os=android"); 175 | c = u.openConnection(); 176 | c.connect(); 177 | BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream())); 178 | br.readLine(); //first line is useless to us 179 | latestVer = br.readLine().split(",")[7]; 180 | br.close(); 181 | }else{ 182 | u = new URL("https://commondatastorage.googleapis.com/chromium-browser-snapshots/Android/LAST_CHANGE"); 183 | c = u.openConnection(); 184 | c.connect(); 185 | BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream())); 186 | latestVer = br.readLine(); 187 | br.close(); 188 | } 189 | log("latest build " + latestVer); 190 | if(cancelRequested){ 191 | throw new IgnorableException("Cancelled by user"); 192 | } 193 | //check currently installed build 194 | String currentVer=""; 195 | try{ 196 | BufferedReader fis=new BufferedReader(new InputStreamReader(getApplicationContext().openFileInput("currentBuild"))); 197 | currentVer=fis.readLine(); 198 | fis.close(); 199 | }catch(Throwable e){ 200 | } 201 | if(cancelRequested){ 202 | throw new IgnorableException("Cancelled by user"); 203 | } 204 | if(currentVer.trim().equals(latestVer.trim())&&!forcedUpdateRequested){ 205 | cancelRequested=true; 206 | throw new IgnorableException("Already on latest build"); 207 | } 208 | //check if we have enough space to update chromium 209 | StatFs stat=new StatFs(sdcard.getAbsolutePath()); 210 | log("free space: "+stat.getAvailableBytes()); 211 | if(stat.getAvailableBytes()=100) throw new Exception("Build number error"); 241 | if(cancelRequested){ 242 | throw new IgnorableException("Cancelled by user"); 243 | } 244 | //download zip of latest build to sdcard 245 | int size=c.getContentLength(),downloaded=0; 246 | FileOutputStream out = new FileOutputStream(new File(sdcard,"chromium.zip")); 247 | log("download started"); 248 | mBuilder.setProgress(100,0,false).setContentText(getString(R.string.notification_downloading)); //set progress bar to 0% and show Downloading 249 | mNotifyManager.notify(1, mBuilder.build()); 250 | long lastNotUpdate=System.nanoTime(); 251 | for (;;) { 252 | if(cancelRequested){ 253 | throw new IgnorableException("Cancelled by user"); 254 | } 255 | byte[] buff = new byte[262144]; 256 | try { 257 | int l = in.read(buff); 258 | if(l==-1) break; 259 | out.write(buff, 0, l); 260 | downloaded+=l; 261 | if(System.nanoTime()-lastNotUpdate>1000000000L){ //every 1s update notification 262 | lastNotUpdate=System.nanoTime(); 263 | log(downloaded+"/"+size+" downloaded"); 264 | mBuilder.setProgress(size,downloaded,false); 265 | mNotifyManager.notify(1, mBuilder.build()); 266 | } 267 | } catch (Exception e) { 268 | if(!(e instanceof EOFException)) throw e; 269 | break; 270 | } 271 | } 272 | in.close(); 273 | out.flush(); 274 | out.close(); 275 | log("download complete"); 276 | if(cancelRequested){ 277 | throw new IgnorableException("Cancelled by user"); 278 | } 279 | mBuilder.setProgress(100,0,true).setContentText(getString(R.string.notification_installing)); //set progress bar to indeterminate and show Installing update 280 | mNotifyManager.notify(1, mBuilder.build()); 281 | //now we have to scan the zip file and extract the APK 282 | log("extracting"); 283 | ZipInputStream zin = new ZipInputStream(new FileInputStream(new File(sdcard,"chromium.zip"))); 284 | ZipEntry z = null; 285 | boolean foundApk=false; 286 | while ((z = zin.getNextEntry()) != null) { 287 | if (z.getName().contains("ChromePublic.apk")) { 288 | log("found apk"); 289 | foundApk=true; 290 | out = new FileOutputStream(new File(sdcard,"chromium.apk")); 291 | for (;;) { 292 | if(cancelRequested){ 293 | throw new IgnorableException("Cancelled by user"); 294 | } 295 | byte[] buff = new byte[262144]; 296 | try { 297 | int l = zin.read(buff); 298 | if(l==-1) break; 299 | out.write(buff, 0, l); 300 | } catch (Exception e) { 301 | break; 302 | } 303 | } 304 | log("apk extracted"); 305 | zin.closeEntry(); 306 | out.close(); 307 | break; 308 | } 309 | } 310 | zin.close(); 311 | mBuilder.mActions.clear(); //remove cancel button 312 | //now that we have the APK we can delete the zip file... 313 | log("deleting zip"); 314 | new File(sdcard,"chromium.zip").delete(); 315 | if(!foundApk) throw new Exception("No apk"); 316 | //and now we can install the apk 317 | String installMethod=prefs.getString("installMethod","auto"); 318 | log("install method pref: "+installMethod); 319 | if(installMethod.equalsIgnoreCase("auto")) installMethod=Utils.isRooted()?"root":"noroot"; 320 | log("install method: "+installMethod); 321 | if(installMethod.equalsIgnoreCase("root")) { 322 | //root: install it silently using some root wizardry 323 | log("installing apk - root"); 324 | String path = new File(sdcard, "chromium.apk").getAbsolutePath(); 325 | log(path); 326 | Process p = Runtime.getRuntime().exec("su"); //create elevated shell 327 | OutputStream os = p.getOutputStream(); 328 | if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P){ 329 | //after oreo, pm can no longer install shit from sdcard (deliberate sabotage of custom roms by google, no doubt), so we replace sdcard with /data/local/tmp and hope for the best 330 | log("moving apk to /data/local/tmp for pie"); 331 | os.write(("mv "+path+" /data/local/tmp\n").getBytes("ASCII")); 332 | os.flush(); 333 | path="/data/local/tmp/chromium.apk"; 334 | } 335 | log("calling pm install"); 336 | os.write(("pm install -r " + path + "\n").getBytes("ASCII")); 337 | os.flush(); //pm install -r chromium.apk 338 | os.write("exit\n".getBytes("ASCII")); 339 | os.flush(); 340 | os.close(); //close elevated shell 341 | p.waitFor(); //wait for it to actually terminate 342 | log("apk installed"); 343 | //chromium is now installed (no real way to be sure actually) and we can delete the APK 344 | log("deleting apk"); 345 | new File(path).delete(); 346 | //show update done notification if enabled 347 | if(prefs.getBoolean("notifyDone",false)) { 348 | NotificationCompat.Builder mBuilder2 = new NotificationCompat.Builder(ChromiumUpdater.this,TAG); 349 | Intent intent=null; 350 | try{ 351 | intent = getPackageManager().getLaunchIntentForPackage("org.chromium.chrome"); 352 | intent.addCategory(Intent.CATEGORY_LAUNCHER); 353 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 354 | }catch (Throwable t){} 355 | mBuilder2.setContentIntent(PendingIntent.getActivity(ChromiumUpdater.this,0,intent,0)); //when tapped, launch chromium 356 | mBuilder2.setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.notifyDone_notification)).setSmallIcon(R.drawable.notification).setAutoCancel(true); 357 | mNotifyManager.notify(2, mBuilder2.build()); 358 | } 359 | }else{ 360 | //no root: show update ready notification 361 | log("installing apk - no root"); 362 | try{ 363 | //now what the fuck is this thing? well in Android 7 they blocked file:// URIs from being passed with intents. This is a workaround. 364 | StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); 365 | StrictMode.setVmPolicy(builder.build()); 366 | }catch (Throwable t){log("err "+t);} 367 | NotificationCompat.Builder mBuilder2 = new NotificationCompat.Builder(ChromiumUpdater.this,TAG); 368 | mBuilder2.setContentTitle(getString(R.string.notification_noroot_ready)).setContentText(getString(R.string.notification_noroot_ready_text)).setSmallIcon(R.drawable.notification).setOngoing(true); 369 | Intent intent=null; 370 | if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) { 371 | intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); 372 | intent.setData(Uri.fromFile(new File(sdcard, "chromium.apk"))); //install apk when notification is clicked 373 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 374 | }else{ 375 | intent = new Intent(Intent.ACTION_VIEW); 376 | intent.setDataAndType(Uri.fromFile(new File(sdcard, "chromium.apk")), "application/vnd.android.package-archive"); //install apk when notification is clicked 377 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 378 | } 379 | mBuilder2.setContentIntent(PendingIntent.getActivity(ChromiumUpdater.this,0,intent,0)); 380 | mBuilder2.setAutoCancel(true); //when clicked, automatically removes the notification 381 | mNotifyManager.notify(2,mBuilder2.build()); 382 | } 383 | try{ 384 | BufferedWriter fos=new BufferedWriter(new OutputStreamWriter(getApplicationContext().openFileOutput("currentBuild", Context.MODE_PRIVATE))); 385 | fos.write(latestVer.trim()); 386 | fos.close(); 387 | }catch(Throwable e){} 388 | } catch (Throwable e) { 389 | //something happened, return 0 (or timestamp if cancelled) 390 | log("err " + e); 391 | ret=0; 392 | if(cancelRequested) ret=Utils.getTimestamp(); 393 | if(!(e instanceof IgnorableException) && getSharedPreferences("chromiumUpdater",MODE_PRIVATE).getBoolean("notifyErrors",false)){ 394 | try{ 395 | NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ChromiumUpdater.this,TAG); 396 | mBuilder.setContentTitle(getString(R.string.error)).setContentText(e.toString()).setSmallIcon(R.drawable.notification); 397 | Intent notifyIntent = new Intent(getApplicationContext(), ShowErrorActivity.class); 398 | notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 399 | notifyIntent.putExtra("message",e.toString()); 400 | try{ 401 | String data="Stack trace:\n"; 402 | for(StackTraceElement x:e.getStackTrace()){ 403 | data+=x.toString()+"\n"; 404 | } 405 | notifyIntent.putExtra("data",data); 406 | }catch(Throwable t){ 407 | notifyIntent.putExtra("data",""); 408 | } 409 | PendingIntent notifyPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, notifyIntent, PendingIntent.FLAG_IMMUTABLE); 410 | mBuilder.setContentIntent(notifyPendingIntent); 411 | mBuilder.setAutoCancel(true); 412 | mNotifyManager.notify(3,mBuilder.build()); 413 | }catch (Throwable t){log("I failed at failing: "+t);} 414 | } 415 | } 416 | try{ 417 | mNotifyManager.cancel(1); 418 | }catch(Throwable t){} 419 | busy=false; 420 | cancelRequested=false; 421 | } 422 | return ret; 423 | } 424 | } 425 | 426 | 427 | @Override 428 | public IBinder onBind(Intent intent) { 429 | return null; 430 | } 431 | 432 | private static BroadcastReceiver broadcastReceiver; 433 | 434 | @Override 435 | public void onCreate() { 436 | try{ 437 | unregisterReceiver(broadcastReceiver); 438 | }catch (Throwable t){} 439 | try { 440 | broadcastReceiver = new Starter(); 441 | registerReceiver(broadcastReceiver, new IntentFilter("android.intent.action.TIME_TICK")); 442 | }catch(Throwable t){} 443 | } 444 | 445 | @Override 446 | public void onDestroy() { 447 | 448 | } 449 | 450 | /* 451 | called when the service is started. creates the UpdateThread if it's not already been created and tells android to restart the service if it dies (sticky) 452 | */ 453 | public int onStartCommand (Intent intent, int flags, int startId){ 454 | if(intent!=null&&intent.getBooleanExtra("forced",false)) forcedUpdateRequested=true; 455 | if(t==null||!t.isAlive()){t=new UpdateThread();t.start();} 456 | return START_STICKY; 457 | } 458 | 459 | } 460 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/EnableNonMarketAppsActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.Intent; 4 | import android.provider.Settings; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | import android.widget.Button; 9 | 10 | public class EnableNonMarketAppsActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_enable_non_market_apps); 16 | Button b=(Button)(findViewById(R.id.button2)); 17 | b.setOnClickListener(new View.OnClickListener() { 18 | @Override 19 | public void onClick(View view) { 20 | Intent intent=new Intent(Settings.ACTION_SECURITY_SETTINGS); 21 | startActivity(intent); 22 | } 23 | }); 24 | checkTPAppsAllowed(); 25 | } 26 | 27 | @Override 28 | protected void onResume() { 29 | super.onResume(); 30 | checkTPAppsAllowed(); 31 | } 32 | 33 | private void checkTPAppsAllowed(){ 34 | if(Utils.thirdPartyAppsAllowed(getContentResolver())){ 35 | Intent i=new Intent(EnableNonMarketAppsActivity.this,MainActivity.class); 36 | startActivity(i); 37 | finish(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/IntroActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import android.support.v4.app.Fragment; 8 | import android.support.v4.app.FragmentManager; 9 | import android.support.v4.app.FragmentPagerAdapter; 10 | import android.support.v4.view.ViewPager; 11 | import android.os.Bundle; 12 | import android.view.LayoutInflater; 13 | import android.view.Menu; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | 18 | import android.widget.Button; 19 | 20 | public class IntroActivity extends AppCompatActivity { 21 | 22 | /** 23 | * The {@link android.support.v4.view.PagerAdapter} that will provide 24 | * fragments for each of the sections. We use a 25 | * {@link FragmentPagerAdapter} derivative, which will keep every 26 | * loaded fragment in memory. If this becomes too memory intensive, it 27 | * may be best to switch to a 28 | * {@link android.support.v4.app.FragmentStatePagerAdapter}. 29 | */ 30 | private SectionsPagerAdapter mSectionsPagerAdapter; 31 | 32 | /** 33 | * The {@link ViewPager} that will host the section contents. 34 | */ 35 | private ViewPager mViewPager; 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_intro); 41 | 42 | // Create the adapter that will return a fragment for each of the three 43 | // primary sections of the activity. 44 | mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); 45 | 46 | // Set up the ViewPager with the sections adapter. 47 | mViewPager = (ViewPager) findViewById(R.id.container); 48 | mViewPager.setAdapter(mSectionsPagerAdapter); 49 | 50 | 51 | } 52 | 53 | 54 | @Override 55 | public boolean onCreateOptionsMenu(Menu menu) { 56 | return false; 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | return super.onOptionsItemSelected(item); 62 | } 63 | 64 | public static class MyFragment extends Fragment { 65 | private static final String ARG_SECTION_NUMBER = "section_number"; 66 | 67 | public MyFragment() { 68 | } 69 | 70 | /** 71 | * Returns a new instance of this fragment for the given section 72 | * number. 73 | */ 74 | public static MyFragment newInstance(int sectionNumber) { 75 | MyFragment fragment = new MyFragment(); 76 | Bundle args = new Bundle(); 77 | args.putInt(ARG_SECTION_NUMBER, sectionNumber); 78 | fragment.setArguments(args); 79 | return fragment; 80 | } 81 | 82 | @Override 83 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 84 | Bundle savedInstanceState) { 85 | if(getArguments().getInt(ARG_SECTION_NUMBER)==1){ 86 | return inflater.inflate(R.layout.fragment_intro, container, false); 87 | } 88 | if(getArguments().getInt(ARG_SECTION_NUMBER)==2){ 89 | return inflater.inflate(R.layout.fragment_intro2, container, false); 90 | } 91 | if(getArguments().getInt(ARG_SECTION_NUMBER)==3){ 92 | return inflater.inflate(R.layout.fragment_intro3, container, false); 93 | } 94 | if(getArguments().getInt(ARG_SECTION_NUMBER)==4){ 95 | View v=inflater.inflate(R.layout.fragment_intro4, container, false); 96 | Button b=(Button)(v.findViewById(R.id.button5)); 97 | b.setOnClickListener(new View.OnClickListener() { 98 | @Override 99 | public void onClick(View view) { 100 | Intent i = new Intent(getActivity(), MainActivity.class); 101 | startActivity(i); 102 | getActivity().finish(); 103 | } 104 | }); 105 | return v; 106 | } 107 | return null; 108 | } 109 | } 110 | 111 | /** 112 | * A {@link FragmentPagerAdapter} that returns a fragment corresponding to 113 | * one of the sections/tabs/pages. 114 | */ 115 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 116 | 117 | public SectionsPagerAdapter(FragmentManager fm) { 118 | super(fm); 119 | } 120 | 121 | @Override 122 | public Fragment getItem(int position) { 123 | return MyFragment.newInstance(position + 1); 124 | } 125 | 126 | @Override 127 | public int getCount() { 128 | return 4; 129 | } 130 | 131 | @Override 132 | public CharSequence getPageTitle(int position) { 133 | return ""; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.Manifest; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.os.PowerManager; 10 | import android.provider.Settings; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.os.Bundle; 14 | import android.view.View; 15 | import android.widget.Button; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | /** 20 | * The welcome screen. It is used to start the service for the first time and to obtain necessary privileges. 21 | */ 22 | public class MainActivity extends AppCompatActivity{ 23 | 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | //check and obtain root privileges 30 | if(!Utils.isRooted()){ 31 | noRootMessage(); 32 | }else{ 33 | try{ 34 | Runtime.getRuntime().exec(new String[]{"su","-c","exit"}).waitFor(); 35 | //we should probably add a check in a later version to make sure the user actually clicked yes to the prompt... 36 | }catch(Throwable e){noRootMessage();} 37 | } 38 | //check and obtain storage write permission 39 | while(!Utils.canWriteToSdcard(this)){ ActivityCompat.requestPermissions(this,new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },1); try{Thread.sleep(1000);}catch(Throwable e){}} 40 | //check if first time starting 41 | try{ 42 | getApplicationContext().openFileInput("firstStart").close(); 43 | //not the first time 44 | }catch (Throwable t){ 45 | //fist time, show intro 46 | try{getApplicationContext().openFileOutput("firstStart",MODE_PRIVATE).close();}catch(Throwable t2){} 47 | Intent i = new Intent(MainActivity.this, IntroActivity.class); 48 | startActivity(i); 49 | finish(); 50 | return; 51 | } 52 | //check if chromium is already installed 53 | try{ 54 | //already checked, no need to show the warning 55 | getApplicationContext().openFileInput("TPChromiumChecked").close(); 56 | }catch(Throwable t) { 57 | //not checked: if chromium is already installed, show warning 58 | try{getApplicationContext().openFileOutput("TPChromiumChecked",MODE_PRIVATE).close();}catch(Throwable t2){} 59 | if (Utils.isPackageInstalled(getApplicationContext(), "org.chromium.chrome")) { 60 | try{ 61 | //it was installed by an older version of this app, it's fine 62 | getApplicationContext().openFileInput("lastUpdate").close(); 63 | }catch(Throwable t2) { 64 | //it was installed by the user or another app, show warning 65 | Intent i = new Intent(MainActivity.this, ChromiumAlreadyInstalledActivity.class); 66 | startActivity(i); 67 | finish(); 68 | return; 69 | } 70 | } 71 | } 72 | //background permissions for SDK26 73 | if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) { 74 | try { 75 | if(!getSystemService(PowerManager.class).isIgnoringBatteryOptimizations(getPackageName())) { 76 | Intent intent = new Intent(); 77 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 78 | intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); 79 | intent.setData(Uri.parse("package:" + getPackageName())); 80 | startActivity(intent); 81 | } 82 | } catch (Throwable t) { 83 | } 84 | } 85 | //start updater service 86 | Intent startServiceIntent = new Intent(getApplicationContext(), ChromiumUpdater.class); 87 | getApplicationContext().startService(startServiceIntent); 88 | //event listener for settings button 89 | findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { 90 | @Override 91 | public void onClick(View view) { 92 | startActivity(new Intent(MainActivity.this,SettingsActivity.class)); 93 | } 94 | }); 95 | //event listener for help button 96 | findViewById(R.id.button6).setOnClickListener(new View.OnClickListener() { 97 | @Override 98 | public void onClick(View view) { 99 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.needHelp_url)))); 100 | } 101 | }); 102 | //event listener for update now button 103 | findViewById(R.id.button7).setOnClickListener(new View.OnClickListener() { 104 | @Override 105 | public void onClick(View view) { 106 | Intent startServiceIntent = new Intent(getApplicationContext(), ChromiumUpdater.class); 107 | startServiceIntent.putExtra("forced",true); 108 | startService(startServiceIntent); 109 | Toast.makeText(getApplicationContext(),getString(R.string.updateNow_clicked),Toast.LENGTH_LONG).show(); 110 | } 111 | }); 112 | } 113 | 114 | @Override 115 | protected void onResume() { 116 | super.onResume(); 117 | SharedPreferences prefs=getSharedPreferences("chromiumUpdater",MODE_PRIVATE); 118 | if(prefs.getBoolean("autoSwitch",true)){ 119 | //if on auto, show regular text and hide the update now button 120 | ((TextView)(findViewById(R.id.textView))).setText(getString(R.string.title)); 121 | ((TextView)(findViewById(R.id.textView4))).setText(getString(R.string.intro)); 122 | ((Button)(findViewById(R.id.button7))).setVisibility(View.INVISIBLE); 123 | }else{ 124 | //if updates is on manual only, show different text and show the update now button 125 | ((TextView)(findViewById(R.id.textView))).setText(getString(R.string.title_manual)); 126 | ((TextView)(findViewById(R.id.textView4))).setText(getString(R.string.intro_manual)); 127 | ((Button)(findViewById(R.id.button7))).setVisibility(View.VISIBLE); 128 | } 129 | } 130 | 131 | private void noRootMessage(){ 132 | ((TextView)(findViewById(R.id.textView))).setText(getString(R.string.noroot_title)); 133 | ((TextView)(findViewById(R.id.textView4))).setText(getString(R.string.noroot_text)); 134 | if(!Utils.thirdPartyAppsAllowed(getContentResolver())){ 135 | Intent i=new Intent(MainActivity.this,EnableNonMarketAppsActivity.class); 136 | startActivity(i); 137 | finish(); 138 | } 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/Settings2Activity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.Intent; 4 | import android.content.SharedPreferences; 5 | import android.os.Bundle; 6 | import android.preference.ListPreference; 7 | import android.preference.Preference; 8 | import android.preference.PreferenceActivity; 9 | import android.preference.PreferenceGroup; 10 | import android.widget.Toast; 11 | 12 | /** 13 | * Settings activity 14 | */ 15 | public class Settings2Activity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { 16 | 17 | @Override 18 | protected void onCreate(Bundle icicle) { 19 | super.onCreate(icicle); 20 | getPreferenceManager().setSharedPreferencesName("chromiumUpdater"); 21 | addPreferencesFromResource(R.xml.pref_advanced); 22 | getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener( 23 | this); 24 | try { 25 | PreferenceGroup p = ((PreferenceGroup) getPreferenceScreen()); 26 | for (int i = 0; i < p.getPreferenceCount(); i++) { 27 | Preference pref = p.getPreference(i); 28 | if (pref instanceof ListPreference) { 29 | ListPreference listPref = (ListPreference) pref; 30 | pref.setSummary(listPref.getEntry()); 31 | } 32 | } 33 | }catch (Throwable t){} 34 | 35 | } 36 | @Override 37 | protected void onResume() { 38 | super.onResume(); 39 | } 40 | @Override 41 | protected void onDestroy() { 42 | getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( 43 | this); 44 | super.onDestroy(); 45 | } 46 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 47 | if(key==null) return; 48 | if(key.equalsIgnoreCase("installMethod")||key.equalsIgnoreCase("channel")){ 49 | Preference pref = findPreference(key); 50 | if (pref instanceof ListPreference) { 51 | ListPreference listPref = (ListPreference) pref; 52 | pref.setSummary(listPref.getEntry()); 53 | } 54 | } 55 | if(key.equalsIgnoreCase("channel")){ 56 | ChromiumUpdater.cancelRequested=true; 57 | while(ChromiumUpdater.isBusy()) try{Thread.sleep(100);}catch(Throwable t){} 58 | Intent startServiceIntent = new Intent(getApplicationContext(), ChromiumUpdater.class); 59 | startServiceIntent.putExtra("forced",true); 60 | startService(startServiceIntent); 61 | Toast.makeText(getApplicationContext(),getString(R.string.updateNow_clicked),Toast.LENGTH_LONG).show(); 62 | } 63 | 64 | } 65 | } -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.os.Bundle; 9 | import android.preference.ListPreference; 10 | import android.preference.Preference; 11 | import android.preference.PreferenceActivity; 12 | import android.preference.PreferenceGroup; 13 | import android.widget.Toast; 14 | 15 | /** 16 | * Settings activity 17 | */ 18 | public class SettingsActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { 19 | 20 | @Override 21 | protected void onCreate(Bundle icicle) { 22 | super.onCreate(icicle); 23 | getPreferenceManager().setSharedPreferencesName("chromiumUpdater"); 24 | addPreferencesFromResource(R.xml.pref_general); 25 | getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener( 26 | this); 27 | try { 28 | PreferenceGroup p = ((PreferenceGroup) getPreferenceScreen()); 29 | for (int i = 0; i < p.getPreferenceCount(); i++) { 30 | Preference pref = p.getPreference(i); 31 | if (pref instanceof ListPreference) { 32 | ListPreference listPref = (ListPreference) pref; 33 | pref.setSummary(listPref.getEntry()); 34 | } 35 | } 36 | }catch (Throwable t){} 37 | //advanced settings listener 38 | ((Preference)findPreference("advancedSettings")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 39 | @Override 40 | public boolean onPreferenceClick(Preference preference) { 41 | startActivity(new Intent(SettingsActivity.this,Settings2Activity.class)); 42 | return true; 43 | } 44 | }); 45 | //github link listener 46 | ((Preference)findPreference("github")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 47 | @Override 48 | public boolean onPreferenceClick(Preference preference) { 49 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github)))); 50 | return true; 51 | } 52 | }); 53 | //update now listener 54 | ((Preference)findPreference("forceUpdate")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 55 | @Override 56 | public boolean onPreferenceClick(Preference preference) { 57 | Intent startServiceIntent = new Intent(getApplicationContext(), ChromiumUpdater.class); 58 | startServiceIntent.putExtra("forced",true); 59 | startService(startServiceIntent); 60 | Toast.makeText(getApplicationContext(),getString(R.string.updateNow_clicked),Toast.LENGTH_LONG).show(); 61 | return true; 62 | } 63 | }); 64 | 65 | //hide app listener 66 | ((Preference)findPreference("hideApp")).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 67 | @Override 68 | public boolean onPreferenceClick(Preference preference) { 69 | PackageManager p = getPackageManager(); 70 | p.setComponentEnabledSetting(new ComponentName(SettingsActivity.this,MainActivity.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); 71 | Toast.makeText(getApplicationContext(),getString(R.string.hide_clicked),Toast.LENGTH_LONG).show(); 72 | try{getApplicationContext().openFileOutput("hidden",MODE_PRIVATE).close();}catch(Throwable t){} 73 | enableDisableOptions(); 74 | return true; 75 | } 76 | }); 77 | 78 | //some options are only active if auto updates are active 79 | enableDisableOptions(); 80 | 81 | } 82 | @Override 83 | protected void onResume() { 84 | super.onResume(); 85 | } 86 | @Override 87 | protected void onDestroy() { 88 | getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( 89 | this); 90 | super.onDestroy(); 91 | } 92 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 93 | enableDisableOptions(); //update grayed out options 94 | if(key.equalsIgnoreCase("updateEvery")){ 95 | //update frequency must be a positive integer 96 | if(Integer.parseInt(sharedPreferences.getString("updateEvery","7"))<1){ 97 | SharedPreferences.Editor e=sharedPreferences.edit(); 98 | e.putString("updateEvery","1"); 99 | e.commit(); 100 | } 101 | } 102 | } 103 | 104 | private void enableDisableOptions(){ 105 | if(getSharedPreferences("chromiumUpdater",MODE_PRIVATE).getBoolean("autoSwitch",true)){ 106 | //when auto updates are enabled, updateEvery, noMobileConnections and hideApp can be changed 107 | findPreference("updateEvery").setEnabled(true); 108 | findPreference("noMobileConnections").setEnabled(true); 109 | findPreference("hideApp").setEnabled(true); 110 | }else{ 111 | //otherwise they're grayed out 112 | findPreference("updateEvery").setEnabled(false); 113 | findPreference("noMobileConnections").setEnabled(false); 114 | findPreference("hideApp").setEnabled(false); 115 | } 116 | //if the application is hidden, hideApp is grayed out 117 | try{ 118 | getApplicationContext().openFileInput("hidden").close(); 119 | ((Preference)findPreference("hideApp")).setEnabled(false); 120 | ((Preference)findPreference("autoSwitch")).setEnabled(false); 121 | }catch (Throwable t){} 122 | } 123 | } -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/ShowErrorActivity.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Build; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.TextView; 11 | 12 | public class ShowErrorActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_show_error); 18 | TextView t=(TextView)(findViewById(R.id.textView6)); 19 | try{ 20 | Intent i=getIntent(); 21 | String message=i.getStringExtra("message"), data=i.getStringExtra("data"); 22 | t.setText(message+"\n\n"+data); 23 | }catch(Throwable e){ 24 | finish(); 25 | } 26 | ((Button)(findViewById(R.id.report))).setOnClickListener(new View.OnClickListener() { 27 | @Override 28 | public void onClick(View view) { 29 | try{ 30 | Intent intent = new Intent(Intent.ACTION_SENDTO); 31 | intent.setData(Uri.parse("mailto:")); 32 | intent.putExtra(Intent.EXTRA_EMAIL, "info@fdossena.com"); 33 | intent.putExtra(Intent.EXTRA_SUBJECT, "Chromium Auto Updater - Bug report"); 34 | intent.putExtra(Intent.EXTRA_TEXT, "\n\n\n\n\n\n---------------------------\nException:"+((TextView)(findViewById(R.id.textView6))).getText()+"\n\n"+"Device model: "+Build.MODEL+"\nSDK Version: "+Build.VERSION.SDK_INT+"\nApp Version: "+getPackageManager().getPackageInfo(getPackageName(), 0).versionCode); 35 | startActivity(Intent.createChooser(intent, "Email")); 36 | }catch(Throwable t){} 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/Starter.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.app.NotificationManager; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.util.Log; 8 | 9 | /** 10 | * This class merely receives events from the system (boot, etc.) and forwards them to the service 11 | */ 12 | public class Starter extends BroadcastReceiver { 13 | private static final String TAG = "Chromium Updater"; 14 | 15 | @Override 16 | public void onReceive(Context context, Intent intent) { 17 | Utils.log("onReceive"); 18 | if(intent.getAction().equals("CANCEL_ACTION")){ 19 | ChromiumUpdater.cancelRequested=true; 20 | try{ 21 | ChromiumUpdater.notifMan.cancel(1); 22 | }catch(Throwable t){} 23 | }else { 24 | try { 25 | Intent startServiceIntent = new Intent(context, ChromiumUpdater.class); 26 | context.startService(startServiceIntent); 27 | }catch(Throwable t){} 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/java/com/dosse/chromiumautoupdater/Utils.java: -------------------------------------------------------------------------------- 1 | package com.dosse.chromiumautoupdater; 2 | 3 | import android.Manifest; 4 | import android.content.ContentResolver; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.net.ConnectivityManager; 8 | import android.net.NetworkInfo; 9 | import android.os.Environment; 10 | import android.provider.Settings; 11 | import android.support.v4.app.ActivityCompat; 12 | import android.util.Log; 13 | 14 | import java.io.BufferedWriter; 15 | import java.io.File; 16 | import java.io.FileOutputStream; 17 | import java.io.OutputStreamWriter; 18 | import java.util.Calendar; 19 | 20 | /** 21 | * This class contains utility methods 22 | */ 23 | 24 | public class Utils { 25 | 26 | /** 27 | * Checks if a package is installed 28 | * @param ctx application context 29 | * @param p package name (e.g. org.chromium.chrome) 30 | * @return true if installed, false otherwise 31 | */ 32 | public static boolean isPackageInstalled(Context ctx, String p){ 33 | try { 34 | ctx.getPackageManager().getPackageInfo(p, PackageManager.GET_META_DATA); 35 | return true; 36 | } catch (Throwable t) { 37 | return false; 38 | } 39 | } 40 | 41 | /** 42 | * Check if the phone is rooted 43 | * @return true if su binary is present, false otherwise 44 | */ 45 | public static boolean isRooted(){ 46 | return rootCheck1()||rootCheck2(); 47 | } 48 | 49 | private static boolean rootCheck1(){ 50 | for (String s : System.getenv("PATH").split(System.getProperty("path.separator"))) { 51 | log("rc1 looking for su in "+s); 52 | if ( new File( s + (s.endsWith("/")?"":"/")+"su" ).exists() ) { 53 | log("found root"); 54 | return true; 55 | } 56 | } 57 | return false; 58 | } 59 | 60 | private static boolean rootCheck2(){ 61 | for (String s : new String[]{"/sbin/", "/system/bin/", "/system/xbin/", "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/"}) { 62 | log("rc2 looking for su in "+s); 63 | if (new File(s + "su").exists()) { 64 | log("found root"); 65 | return true; 66 | } 67 | } 68 | return false; 69 | } 70 | 71 | public static boolean thirdPartyAppsAllowed(ContentResolver cr){ 72 | try { 73 | return Settings.Secure.getInt(cr, Settings.Secure.INSTALL_NON_MARKET_APPS)!=0; 74 | } catch (Throwable e) { 75 | return true; 76 | } 77 | } 78 | 79 | /** 80 | * Checks for storage write permissions 81 | * @param ctx context 82 | * @return true if permission is granted, false otherwise 83 | */ 84 | public static boolean canWriteToSdcard(Context ctx){ 85 | return ActivityCompat.checkSelfPermission(ctx, Manifest.permission.WRITE_EXTERNAL_STORAGE)== PackageManager.PERMISSION_GRANTED; 86 | } 87 | 88 | /** 89 | * Checks if an internet connection is available 90 | * @param ctx context 91 | * @return true if available, false otherwise 92 | */ 93 | public static boolean isConnected(Context ctx){ 94 | NetworkInfo info=((ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); 95 | return info!=null&&info.isConnected(); 96 | } 97 | 98 | /** 99 | * Checks if the main internet connection is mobile 100 | * @param ctx context 101 | * @return true if available, false if not mobile or not connected 102 | */ 103 | public static boolean isMobileConnection(Context ctx){ 104 | NetworkInfo info=((ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo(); 105 | return info!=null&&info.getType()==ConnectivityManager.TYPE_MOBILE; 106 | } 107 | 108 | 109 | /** 110 | * Timestamp function in ms 111 | * @return utc time in ms 112 | */ 113 | public static long getTimestamp(){ 114 | return Calendar.getInstance().getTimeInMillis(); 115 | } 116 | 117 | public static final boolean USE_LOG= BuildConfig.DEBUG; //logging is only active in debug builds 118 | public static final boolean USE_LOGFILE=false; //if active, log will be stored on /sdcard/chrupd.log 119 | 120 | /** 121 | * Log 122 | * @param s String 123 | */ 124 | public static void log(String s){ 125 | if(USE_LOG) Log.d("Chromium Updater",s); 126 | if(USE_LOGFILE){ 127 | try { 128 | File sdcard = Environment.getExternalStorageDirectory(); 129 | File logFile = new File(sdcard, "chrupd.log"); 130 | BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(logFile, true))); 131 | w.write(getTimestamp()+" - "+s+"\n"); 132 | w.flush(); 133 | w.close(); 134 | }catch(Throwable t){ 135 | } 136 | } 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/drawable/introbk.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/drawable/introbk2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/drawable/introbk3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/drawable/introbk4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/drawable/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adolfintel/chromiumUpdater/7ef9077c8d23307b43290652fac3933431bc1a62/ChromiumAutoUpdater/app/src/main/res/drawable/notification.png -------------------------------------------------------------------------------- /ChromiumAutoUpdater/app/src/main/res/layout-v23/activity_show_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 21 | 22 |