{"id":2049,"date":"2019-02-06T14:42:21","date_gmt":"2019-02-06T12:42:21","guid":{"rendered":"https:\/\/upcenter.de\/wordpress\/?p=2049"},"modified":"2019-02-06T14:44:45","modified_gmt":"2019-02-06T12:44:45","slug":"android-speichern-ohne-genehmigung","status":"publish","type":"post","link":"https:\/\/upcenter.de\/wordpress\/android-speichern-ohne-genehmigung\/","title":{"rendered":"Android: Speichern ohne Genehmigung"},"content":{"rendered":"\n<p>Ein Appetizer aus meinem in K\u00fcrze erscheinenden Buch: <a href=\"https:\/\/www.rheinwerk-verlag.de\/android-apps-entwickeln-fur-einsteiger_4869\/\">Android-Apps entwickeln f\u00fcr Einsteiger (8. Auflage)<\/a>.<\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"alignright\"><img loading=\"lazy\" decoding=\"async\" width=\"325\" height=\"267\" src=\"https:\/\/upcenter.de\/wordpress\/wp-content\/uploads\/cover.png\" alt=\"\" class=\"wp-image-2052\" srcset=\"https:\/\/upcenter.de\/wordpress\/wp-content\/uploads\/cover.png 325w, https:\/\/upcenter.de\/wordpress\/wp-content\/uploads\/cover-300x246.png 300w\" sizes=\"auto, (max-width: 325px) 100vw, 325px\" \/><\/figure><\/div>\n\n\n\n<p>\nVon Ihrem PC kennen Sie den \u00bbSpeichern unter\u00ab-Dialog. Jede\ninstallierte Anwendung funktioniert in dieser Hinsicht gleich: Sie\n\u00fcberl\u00e4sst Ihnen als Nutzer die Entscheidung, wo ein Dokument landen\nsoll und unter welchem Namen.<\/p>\n\n\n\n<p>Tats\u00e4chlich existiert ein solcher Dialog auch unter Android, blo\u00df verwenden ihn nur wenige Apps. Dabei hat das sich dahinter verbergende <em><strong><a href=\"https:\/\/developer.android.com\/guide\/topics\/providers\/document-provider\">Storage Access Framework<\/a><\/strong><\/em> sogar einen immensen Vorteil: Es kommt ohne die Genehmigung zum Speichern von Dateien auf der SD-Karte aus!<\/p>\n\n\n\n<p> Lassen Sie uns als Beispiel einen Mini-Editor schreiben, der es dem Nutzer erlaubt, einen Text einzugeben und ihn mittels Storage Access Framework zu speichern. Dieser bescheidene Funktionsumfang rechtfertigt eigentlich nur den Projektnamen <strong>MiniMiniEditor<\/strong>.<\/p>\n\n\n\n<p>Achten Sie darauf, beim Anlegen des Projekts die minSdkVersion auf 19 zu stellen (Android 4.4), denn zuvor existierte das Storage Access Framework noch nicht.<\/p>\n\n\n\n<p>\nSchalten Sie au\u00dferdem die Unterst\u00fctzung f\u00fcr Java 8 ein, da die\nfolgenden Codezeilen eine Methodenreferenz verwenden.<\/p>\n\n\n\n<p> Verwenden Sie im Layout <strong>activity_main.xml<\/strong> ein vertikales LinearLayout, und setzen Sie einen Speichern-Button und ein EditText hinein. Damit letztere View den gesamten verf\u00fcgbaren Platz ausf\u00fcllt und mehrzeiligen Text entgegennimmt, setzen Sie die Attribute wie folgt:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> &lt;EditText android:id=\"@+id\/text\"  android:layout_width=\"match_parent\" android:layout_height=\"0dp\"  android:gravity=\"top|left\"  ndroid:layout_weight=\"1\" android:inputType=\"textMultiLine\" \/&gt;<\/pre>\n\n\n\n<p>\nIn der Klasse MainActivity\nverkn\u00fcpfen Sie zun\u00e4chst den EditText\nmit der festgelegten\nID text\nmit einem Attribut editText\nund den Button mit einem Listener:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">editText = findViewById(R.id.text); <br>findViewById(R.id.save).setOnClickListener(this::onSaveClicked);<\/pre>\n\n\n\n<p>\nWenn der Nutzer auf den <strong>Speichern<\/strong>-Button\ndr\u00fcckt, basteln Sie ein spezielles Intent-Objekt:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Intent intent = new Intent(<strong>Intent.ACTION_CREATE_DOCUMENT<\/strong>); <br>intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setType(\"text\/plain\");<br>intent.putExtra(Intent.EXTRA_TITLE, \"\");<br>startActivityForResult(intent, REQUESTCODE_SAVE);<\/pre>\n\n\n\n<p>\nEntscheidend ist hier die Action ACTION_CREATE_DOCUMENT.\nAls EXTRA_TITLE\nk\u00f6nnen Sie einen Dateinamen vorgeben, den der Nutzer noch \u00e4ndern\nkann.<\/p>\n\n\n\n<p>\nWas Sie hier nicht sehen, ist der eingegebene Text: Der wird nicht\netwa an den Intent geh\u00e4ngt, sondern in einem zweiten Schritt\ngespeichert. Dadurch erh\u00e4lt die Dateiauswahl nie Zugriff auf die\nfraglichen Daten. F\u00fcr das eigentliche Speichern sind Sie selbst\nzust\u00e4ndig.<\/p>\n\n\n\n<p>\nDeshalb starten Sie den Intent mit startActivityForResult()\nund \u00fcbergeben einen frei definierbaren Request-Code.<\/p>\n\n\n\n<p>\nDer Storage Access Provider horcht mit einem Intent-Filter darauf und\npr\u00e4sentiert dem Nutzer einen Auswahldialog.<\/p>\n\n\n\n<p> Standardm\u00e4\u00dfig zeigt der <strong>Speichern unter<\/strong>-Dialog nicht viele m\u00f6gliche Ziele an: meist nur das <strong>Downloads<\/strong>-Verzeichnis und Ihr <strong>Google Drive<\/strong>, falls vorhanden. Aber hinter dem Zahnrad rechts oben erreichen Sie den Einstellungen-Dialog, wo Sie mit <strong>Erweiterte Ger\u00e4te anzeigen<\/strong> daf\u00fcr sorgen k\u00f6nnen, dass auch Ihr interner Speicher sowie eine eventuell eingesetzte SD-Karte erscheinen. Die meisten neueren Android-Ger\u00e4te zeigen hier auch USB-Speichermedien an, die Sie einst\u00f6pseln.<\/p>\n\n\n\n<p>\nSobald der Nutzer das gew\u00fcnschte Ziel ausgew\u00e4hlt hat, sendet das\nStorage Access Framework Ihrer Activity ein Ergebnis. Das nehmen Sie\nentgegen, indem Sie die Methode onActivityResult()\n\u00fcberschreiben:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> public void onActivityResult(int requestCode, int resultCode, Intent resultData) { }<\/pre>\n\n\n\n<p>\nDa an dieser Stelle grunds\u00e4tzlich ganz unterschiedliche Mitteilungen\neintreffen k\u00f6nnen, m\u00fcssen Sie den requestCode\nmit Ihrer Definition vergleichen. Der resultCode\nverr\u00e4t Ihnen, ob der Nutzer den Vorgang erfolgreich beendet hat \u2013\nnur dann m\u00f6chten Sie etwas speichern:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if(requestCode==REQUESTCODE_SAVE\n&amp;&amp; resultCode==Activity.RESULT_OK) {\n}<\/pre>\n\n\n\n<p>\nDas Storage Access Framework \u00fcbergibt Ihnen im Intent resultData\ndas Ziel f\u00fcr Ihre Datei, und zwar in Form einer URI:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Uri\nuri = resultData.getData();<\/pre>\n\n\n\n<p>\nDiese URI zeigt auf einen Dateipfad, aber dessen genauer Ort muss Sie\nnicht interessieren. Sie verwenden lediglich den ContentResolver,\num sich einen OutputStream\nzu beschaffen, der Ihre Daten an die richtige Stelle schreibt. Das\nStorage Access Framework sorgt daf\u00fcr, dass Sie tempor\u00e4r die n\u00f6tigen\nSchreibrechte besitzen, obwohl Ihre App keinerlei Genehmigung zum\nSchreiben auf den Datentr\u00e4ger besitzt.<\/p>\n\n\n\n<p>\nDer Schreibvorgang funktioniert im Fall von Textdaten am einfachsten\n\u00fcber einen PrintWriter:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> try {<br>   OutputStream stream = getContentResolver().openOutputStream(uri);<br>   PrintWriter writer = new PrintWriter(stream);<br>   writer.write(editText.getText().toString());<br>   writer.flush();<br>   stream.close();<br> } catch (java.io.IOException e) {<br>   Log.e(getLocalClassName(),\"caught IOException\",e);<br> }<\/pre>\n\n\n\n<p>\nDas war schon alles. Nat\u00fcrlich k\u00f6nnen Sie dem Nutzer jetzt noch\neine Erfolgsmeldung zeigen (und im Fehlerfall eine Fehlermeldung),\naber das \u00fcberlasse ich Ihnen.<\/p>\n\n\n\n<p> Das Storage Access Framework kann auch dazu dienen, vorhandene Dateien zum \u00d6ffnen auszuw\u00e4hlen, z.&nbsp;B? Mediendateien. Als Intent-Action verwenden Sie dann ACTION_OPEN_DOCUMENT, der Rest funktioniert analog \u2013 ebenfalls ohne irgendwelche Permissions. Letztlich entscheidet der Nutzer im Einzelfall dar\u00fcber, auf welche Dateien Ihre App zugreifen darf, deshalb ist die allgemeine Permission verzichtbar \u2013 das ist durchaus manchmal ein Vorteil, denn viele skeptische Nutzer installieren nur Apps, die m\u00f6glichst wenige Genehmigungen einfordern.<\/p>\n\n\n\n<p>Diese und viele andere n\u00fctzliche Tipps finden Sie in meinem Buch:  <a href=\"https:\/\/www.rheinwerk-verlag.de\/android-apps-entwickeln-fur-einsteiger_4869\/\">Android-Apps entwickeln f\u00fcr Einsteiger (8. Auflage),&nbsp;erschienen&nbsp;im&nbsp;Rheinwerk-Verlag<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ein Appetizer aus meinem in K\u00fcrze erscheinenden Buch: Android-Apps entwickeln f\u00fcr Einsteiger (8. Auflage). Von Ihrem PC kennen Sie den \u00bbSpeichern unter\u00ab-Dialog. Jede installierte Anwendung funktioniert in dieser Hinsicht gleich: Sie \u00fcberl\u00e4sst Ihnen als Nutzer die Entscheidung, wo ein Dokument landen soll und unter welchem Namen. Tats\u00e4chlich existiert ein solcher Dialog auch unter Android, blo\u00df &hellip; <a href=\"https:\/\/upcenter.de\/wordpress\/android-speichern-ohne-genehmigung\/\" class=\"more-link\"><span class=\"screen-reader-text\">Android: Speichern ohne Genehmigung<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[21],"class_list":["post-2049","post","type-post","status-publish","format-standard","hentry","category-besser-coden","tag-android"],"_links":{"self":[{"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/posts\/2049","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/comments?post=2049"}],"version-history":[{"count":5,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/posts\/2049\/revisions"}],"predecessor-version":[{"id":2057,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/posts\/2049\/revisions\/2057"}],"wp:attachment":[{"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/media?parent=2049"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/categories?post=2049"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/upcenter.de\/wordpress\/wp-json\/wp\/v2\/tags?post=2049"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}