Differences

This shows you the differences between two versions of the page.

Link to this comparison view

smd:laboratoare:06 [2019/04/08 20:04]
adriana.draghici [Task 1 - Runtime permission check]
smd:laboratoare:06 [2021/05/20 21:30] (current)
adriana.draghici [Shared preferences]
Line 18: Line 18:
  
 For sharing data to external apps, you can use one of the main Android components: [[https://​developer.android.com/​guide/​topics/​providers/​content-provider-basics|ContentProviders]]. For files, this component is extended by the [[https://​developer.android.com/​reference/​android/​support/​v4/​content/​FileProvider.html|FileProvider]]. For sharing data to external apps, you can use one of the main Android components: [[https://​developer.android.com/​guide/​topics/​providers/​content-provider-basics|ContentProviders]]. For files, this component is extended by the [[https://​developer.android.com/​reference/​android/​support/​v4/​content/​FileProvider.html|FileProvider]].
 +
 +<note important>​Android API Level 29 introduced important changes to how storage is accessed, driven by the need for increased privacy.
 +It provided **Scoped Storage** ([[https://​developer.android.com/​training/​data-storage#​scoped-storage|documentation]],​ [[https://​www.raywenderlich.com/​9577211-scoped-storage-in-android-10-getting-started|tutorial]]). Api Level 30 also introduced some updates related to storage, check [[https://​developer.android.com/​about/​versions/​11/​privacy/​storage|this link]] for some information about them. </​note>​
  
 ====Internal Storage==== ====Internal Storage====
Line 45: Line 48:
    * Reading and writing to it requires the app to declare ''​WRITE_EXTERNAL_STORAGE''​ or ''​READ_EXTERNAL_STORAGE''​ **permissions** (declaring the write permission automatically grants the read permission as well). The next Android version, Q, deprecates these.    * Reading and writing to it requires the app to declare ''​WRITE_EXTERNAL_STORAGE''​ or ''​READ_EXTERNAL_STORAGE''​ **permissions** (declaring the write permission automatically grants the read permission as well). The next Android version, Q, deprecates these.
       * accessing public external files requires the permissions       * accessing public external files requires the permissions
-      * accessing private external files do not requires ​the permissions from API Level 18 (Android 4.4 - KitKat). +      * accessing private external files does not require ​the permissions from API Level 18 (Android 4.4 - KitKat). 
-   * **__Security tips:__**+<note important>​**__Security tips:__**
      * validate the data read from external storage      * validate the data read from external storage
      * encrypt sensitive data stored in external storage      * encrypt sensitive data stored in external storage
-     * decide ​wether ​the data should be persisted after the app is installed or not.+     * decide ​whether ​the data should be persisted after the app is installed or not.
      * ask for the permissions at runtime, preferably showing a custom message informing the user why do you need those permissions.      * ask for the permissions at runtime, preferably showing a custom message informing the user why do you need those permissions.
 +</​note>​
 **How-to:** **How-to:**
    * Declare the permissions in the manifest (if needed)    * Declare the permissions in the manifest (if needed)
    * Obtain the path using one of the following methods provided by the [[https://​developer.android.com/​reference/​android/​os/​Environment.html|Environment]] class or Context classes:    * Obtain the path using one of the following methods provided by the [[https://​developer.android.com/​reference/​android/​os/​Environment.html|Environment]] class or Context classes:
-     * [[https://​developer.android.com/​reference/​android/​os/​Environment.html#​getExternalStoragePublicDirectory(java.lang.String)|Environment.getExternalStoragePublicDirectory]] - returns the path to the public external directory 
      * [[https://​developer.android.com/​reference/​android/​content/​Context.html#​getExternalFilesDir(java.lang.String)|Context.getExternalFilesDir()]] - returns the path to the private external directory      * [[https://​developer.android.com/​reference/​android/​content/​Context.html#​getExternalFilesDir(java.lang.String)|Context.getExternalFilesDir()]] - returns the path to the private external directory
      * [[https://​developer.android.com/​reference/​android/​content/​Context.html#​getExternalFilesDirs(java.lang.String)|Context.getExternalFilesDirs]] - returns an array of paths to the external directories (e.g. you can have two directories if an sd card is present).      * [[https://​developer.android.com/​reference/​android/​content/​Context.html#​getExternalFilesDirs(java.lang.String)|Context.getExternalFilesDirs]] - returns an array of paths to the external directories (e.g. you can have two directories if an sd card is present).
Line 63: Line 65:
  
 <code Java> <code Java>
 + // Code working on versions < Q (Android10 aka API Level 29)
 +
  ​String path = Environment.getExternalStorageDirectory().toString();​  ​String path = Environment.getExternalStorageDirectory().toString();​
  File file = new File(path, "​image.png"​);​  File file = new File(path, "​image.png"​);​
Line 75: Line 79:
 </​code>​ </​code>​
  
-[[https://developer.android.com/training/​data-storage/files.html#WriteExternalStorage|Code tutorial]]+From Android 10 you need to use ContentResolver like in these examples: ​[[https://www.raywenderlich.com/10217168-preparing-for-scoped-storage|Creating a new file]], [[https://​stackoverflow.com/​questions/​56468539/​getexternalstoragepublicdirectory-deprecated-in-android-q/​57649669#57649669|ContentResolver SO thread]]
    
  
Line 89: Line 93:
 sharedPref = this.getSharedPreferences("​mysettings",​ Context.MODE_PRIVATE);​ sharedPref = this.getSharedPreferences("​mysettings",​ Context.MODE_PRIVATE);​
 // or // or
-sharedPref = PreferenceManager.getDefaultSharedPreferences(context);​+sharedPref = PreferenceManager.getDefaultSharedPreferences(context); ​//!!! recently deprecated in API level 29
 </​code>​ </​code>​
  
Line 103: Line 107:
 editor.putInt("​my_int",​ 0); editor.putInt("​my_int",​ 0);
  
 +// Must save them!
 editor.apply(); ​ //async write to disc editor.apply(); ​ //async write to disc
 // or use // or use
Line 124: Line 129:
 ==== Task 0 - Init ==== ==== Task 0 - Init ====
  
-Create a new Android application project. ​Download ​the lab archive and use its content, or start with your own UI design.+Create a new Android application project. ​You can download ​the lab archive and use its layout, or start with your own UI design.
  
 ==== Task 1 - Runtime permission check ==== ==== Task 1 - Runtime permission check ====
Line 140: Line 145:
 Implement a button listener which triggers an image download from a given URL and stores it on //external storage//. Implement a button listener which triggers an image download from a given URL and stores it on //external storage//.
   * Image url example: https://​swarm.cs.pub.ro/​~adriana/​smd/​android-kotlin.png   * Image url example: https://​swarm.cs.pub.ro/​~adriana/​smd/​android-kotlin.png
-  * Combine the knowledge from [[smd:​laboratoare:​04|Lab 4]] and [[smd:​laboratoare:​05|Lab 5]] for downloading content: use HttpsURLConnection inside of a Runnable ​or an AsyncTask+  * Combine the knowledge from [[smd:​laboratoare:​04|Lab 4]] and [[smd:​laboratoare:​05|Lab 5]] for downloading content: use HttpsURLConnection inside of a Runnable. 
-  * You can handle the image using [[https://​developer.android.com/​reference/​android/​graphics/​Bitmap|Bitmap]] and [[https://​developer.android.com/​reference/​android/​graphics/​BitmapFactory|BitmapFactory]] as in the code snippets provided at the end of this section+  * You can handle the image using [[https://​developer.android.com/​reference/​android/​graphics/​Bitmap|Bitmap]] and [[https://​developer.android.com/​reference/​android/​graphics/​BitmapFactory|BitmapFactory]] as in the code snippets provided at the [[https://​ocw.cs.pub.ro/​courses/​smd/​laboratoare/​06#​task_5_-_shared_preferences|end of this section]].
   * Obtain the path to the external storage using one of the methods described in the [[smd:​laboratoare:​06#​external_storage|External Storage]] section. ​   * Obtain the path to the external storage using one of the methods described in the [[smd:​laboratoare:​06#​external_storage|External Storage]] section. ​
-  * Log the path of the file+  * Log the path of the file.
   * Try to access the image from your phone'​s Files app or from command line (connect to your device using ''​adb shell''​).   * Try to access the image from your phone'​s Files app or from command line (connect to your device using ''​adb shell''​).
  
Line 155: Line 160:
 Provide the functionality from tasks 1 and 2 using internal storage instead of external. Provide the functionality from tasks 1 and 2 using internal storage instead of external.
   * You can choose to store the file using one of the methods described in the [[smd:​laboratoare:​06##​internal_storage|Internal Storage]] section. ​   * You can choose to store the file using one of the methods described in the [[smd:​laboratoare:​06##​internal_storage|Internal Storage]] section. ​
-  * Log the path of the file, try to access it from the command line.+  * Log the path of the file, try to access it from the command line (adb shell).
  
 ==== Task 5 - Shared preferences ==== ==== Task 5 - Shared preferences ====
Line 165: Line 170:
   * When the user selects an item from the spinner, store it as a shared preference. Use [[https://​developer.android.com/​reference/​android/​widget/​AdapterView.html#​setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)|setOnItemSelectedListener]].   * When the user selects an item from the spinner, store it as a shared preference. Use [[https://​developer.android.com/​reference/​android/​widget/​AdapterView.html#​setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)|setOnItemSelectedListener]].
   * When the user clicks Download, use an external or internal path, depending on the value stored in the shared preferences. If the value is "​Undefined",​ show a toast telling the user to choose a type of storage.   * When the user clicks Download, use an external or internal path, depending on the value stored in the shared preferences. If the value is "​Undefined",​ show a toast telling the user to choose a type of storage.
 +
 +<spoiler Example >
 +<code java>
 +    private String getStorageType() {
 +        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);​
 +        return sharedPref.getString(getString(R.string.storage_type_key),​ "​undefined"​);​
 +    }
 +
 +    private void storeStorageType(String type)
 +        SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();​
 +        editor.putString(getString(R.string.storage_type_key),​ type);
 +        editor.apply();​
 +    }
 +
 +    private class StorageTypeSelectedListener implements AdapterView.OnItemSelectedListener {
 +
 +        @Override
 +        public void onItemSelected(AdapterView<?>​ adapterView,​ View view, int i, long l) {
 +            String selectedItem = adapterView.getItemAtPosition(i).toString();​
 +            Log.d(TAG, "​Selected storage type: " +  selectedItem);​
 +            storeStorageType(selectedItem);​
 +        }
 +
 +        @Override
 +        public void onNothingSelected(AdapterView<?>​ adapterView) {
 +        }
 +    }
 +    </​code>​
 +</​spoiler>​
   ​   ​
 Image manipulation code snippets: Image manipulation code snippets:
Line 170: Line 204:
 // to obtain a Bitmap object from an InputStream // to obtain a Bitmap object from an InputStream
 BitmapFactory.decodeStream(input);​ BitmapFactory.decodeStream(input);​
 +
 +// to save a Bitmap object in an OutputStream
 +bitmap.compress(compressFormat,​ compressionRate,​ outputStream);​
  
 // to read a Bitmap from a file // to read a Bitmap from a file
Line 188: Line 225:
 ===== Resources & Useful Links===== ===== Resources & Useful Links=====
   * [[https://​developer.android.com/​guide/​topics/​data/​data-storage|Android Data Storage documentation]]   * [[https://​developer.android.com/​guide/​topics/​data/​data-storage|Android Data Storage documentation]]
-  * [[https://​developer.android.com/​training/data-storage/files.html|Save files on device ​storage]] +  * [[https://​developer.android.com/​about/versions/​11/​privacy/storage|Storage updates in Android 11]] 
-  * {{lab6-skel.zip|}}+  * {{lab6_skel.zip|}}
  
smd/laboratoare/06.1554743085.txt.gz · Last modified: 2019/04/08 20:04 by adriana.draghici
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0