mirror of https://github.com/kurisufriend/Clover
parent
5e09cd398b
commit
3db2ac07de
@ -0,0 +1,63 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.presenter; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Intent; |
||||||
|
|
||||||
|
import org.floens.chan.core.storage.Storage; |
||||||
|
import org.floens.chan.ui.activity.ActivityResultHelper; |
||||||
|
|
||||||
|
import javax.inject.Inject; |
||||||
|
|
||||||
|
public class StorageSetupPresenter { |
||||||
|
private Callback callback; |
||||||
|
|
||||||
|
private Storage storage; |
||||||
|
private ActivityResultHelper results; |
||||||
|
|
||||||
|
@Inject |
||||||
|
public StorageSetupPresenter(Storage storage, ActivityResultHelper results) { |
||||||
|
this.storage = storage; |
||||||
|
this.results = results; |
||||||
|
} |
||||||
|
|
||||||
|
public void create(Callback callback) { |
||||||
|
this.callback = callback; |
||||||
|
|
||||||
|
updateDescription(); |
||||||
|
} |
||||||
|
|
||||||
|
public void saveLocationClicked() { |
||||||
|
Intent openTreeIntent = storage.getOpenTreeIntent(); |
||||||
|
results.getResultFromIntent(openTreeIntent, (resultCode, result) -> { |
||||||
|
if (resultCode == Activity.RESULT_OK) { |
||||||
|
storage.handleOpenTreeIntent(result); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void updateDescription() { |
||||||
|
String description = storage.currentStorageName(); |
||||||
|
callback.setSaveLocationDescription(description); |
||||||
|
} |
||||||
|
|
||||||
|
public interface Callback { |
||||||
|
void setSaveLocationDescription(String description); |
||||||
|
} |
||||||
|
} |
@ -1,38 +0,0 @@ |
|||||||
package org.floens.chan.core.storage; |
|
||||||
|
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.net.Uri; |
|
||||||
|
|
||||||
public class BaseStorageImpl implements StorageImpl { |
|
||||||
protected Context applicationContext; |
|
||||||
|
|
||||||
public BaseStorageImpl(Context applicationContext) { |
|
||||||
this.applicationContext = applicationContext; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean supportsExternalStorage() { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Intent getOpenTreeIntent() { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void handleOpenTreeIntent(Uri uri) { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public StorageFile obtainStorageFileForName(String name) { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String currentStorageName() { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
} |
|
@ -1,106 +0,0 @@ |
|||||||
package org.floens.chan.core.storage; |
|
||||||
|
|
||||||
import android.content.ContentResolver; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.database.Cursor; |
|
||||||
import android.net.Uri; |
|
||||||
import android.os.Build; |
|
||||||
import android.provider.DocumentsContract; |
|
||||||
import android.support.annotation.RequiresApi; |
|
||||||
|
|
||||||
import org.floens.chan.core.settings.ChanSettings; |
|
||||||
import org.floens.chan.utils.IOUtils; |
|
||||||
import org.floens.chan.utils.Logger; |
|
||||||
|
|
||||||
import java.io.FileNotFoundException; |
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) |
|
||||||
public class LollipopStorageImpl extends BaseStorageImpl { |
|
||||||
private static final String TAG = "LollipopStorageImpl"; |
|
||||||
|
|
||||||
public LollipopStorageImpl(Context applicationContext) { |
|
||||||
super(applicationContext); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean supportsExternalStorage() { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Intent getOpenTreeIntent() { |
|
||||||
return new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void handleOpenTreeIntent(Uri uri) { |
|
||||||
String documentId = DocumentsContract.getTreeDocumentId(uri); |
|
||||||
Uri treeDocumentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId); |
|
||||||
|
|
||||||
ChanSettings.saveLocationTreeUri.set(treeDocumentUri.toString()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public StorageFile obtainStorageFileForName(String name) { |
|
||||||
String uriString = ChanSettings.saveLocationTreeUri.get(); |
|
||||||
if (uriString.isEmpty()) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
Uri treeUri = Uri.parse(uriString); |
|
||||||
|
|
||||||
ContentResolver contentResolver = applicationContext.getContentResolver(); |
|
||||||
|
|
||||||
String documentId = DocumentsContract.getTreeDocumentId(treeUri); |
|
||||||
Uri treeDocumentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId); |
|
||||||
|
|
||||||
Uri docUri; |
|
||||||
try { |
|
||||||
docUri = DocumentsContract.createDocument(contentResolver, treeDocumentUri, |
|
||||||
"text", name); |
|
||||||
} catch (FileNotFoundException e) { |
|
||||||
Logger.e(TAG, "obtainStorageFileForName createDocument", e); |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
return StorageFile.fromUri(contentResolver, docUri); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String currentStorageName() { |
|
||||||
String uriString = ChanSettings.saveLocationTreeUri.get(); |
|
||||||
if (uriString.isEmpty()) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
Uri treeUri = Uri.parse(uriString); |
|
||||||
return queryTreeName(treeUri); |
|
||||||
} |
|
||||||
|
|
||||||
private String queryTreeName(Uri uri) { |
|
||||||
ContentResolver contentResolver = applicationContext.getContentResolver(); |
|
||||||
|
|
||||||
Cursor c = null; |
|
||||||
String name = null; |
|
||||||
try { |
|
||||||
c = contentResolver.query(uri, new String[]{ |
|
||||||
DocumentsContract.Document.COLUMN_DISPLAY_NAME, |
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE |
|
||||||
}, null, null, null); |
|
||||||
|
|
||||||
if (c != null && c.moveToNext()) { |
|
||||||
name = c.getString(0); |
|
||||||
// mime = c.getString(1);
|
|
||||||
} |
|
||||||
|
|
||||||
return name; |
|
||||||
} catch (Exception e) { |
|
||||||
Logger.e(TAG, "queryTreeName", e); |
|
||||||
} finally { |
|
||||||
IOUtils.closeQuietly(c); |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package org.floens.chan.core.storage; |
|
||||||
|
|
||||||
import android.content.Intent; |
|
||||||
import android.net.Uri; |
|
||||||
|
|
||||||
public interface StorageImpl { |
|
||||||
boolean supportsExternalStorage(); |
|
||||||
|
|
||||||
Intent getOpenTreeIntent(); |
|
||||||
|
|
||||||
void handleOpenTreeIntent(Uri uri); |
|
||||||
|
|
||||||
StorageFile obtainStorageFileForName(String name); |
|
||||||
|
|
||||||
String currentStorageName(); |
|
||||||
} |
|
@ -0,0 +1,156 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.ui.activity; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.util.SparseArray; |
||||||
|
|
||||||
|
import org.floens.chan.utils.Logger; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import javax.inject.Inject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Abstraction of the startActivityForResult and onActivityResult calls. It autogenerates |
||||||
|
* a result code and adds a callback interface. Activities must implement the |
||||||
|
* ActivityResultStarter interface and the application must implement the |
||||||
|
* ApplicationActivitiesProvider interface. |
||||||
|
*/ |
||||||
|
public class ActivityResultHelper { |
||||||
|
private static final String TAG = "ActivityResultHelper"; |
||||||
|
|
||||||
|
private Context applicationContext; |
||||||
|
|
||||||
|
private int resultCounter = 100; |
||||||
|
|
||||||
|
@Inject |
||||||
|
public ActivityResultHelper(Context applicationContext) { |
||||||
|
this.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
public void getResultFromIntent(Intent intent, ActivityResultCallback callback) { |
||||||
|
resultCounter++; |
||||||
|
|
||||||
|
ActivityResultStarter starter = findStarter(); |
||||||
|
if (starter == null) { |
||||||
|
Logger.e(TAG, "Could not find an active activity to use."); |
||||||
|
callback.onActivityResult(Activity.RESULT_CANCELED, null); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
starter.startActivityForResultWithCallback(intent, resultCounter, callback); |
||||||
|
} |
||||||
|
|
||||||
|
private ActivityResultStarter findStarter() { |
||||||
|
List<Activity> activities = ((ApplicationActivitiesProvider) applicationContext).getActivities(); |
||||||
|
List<ActivityResultStarter> starters = new ArrayList<>(1); |
||||||
|
for (Activity activity : activities) { |
||||||
|
if (activity instanceof ActivityResultStarter) { |
||||||
|
starters.add((ActivityResultStarter) activity); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (starters.isEmpty()) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
if (starters.size() > 1) { |
||||||
|
// Give priority to the resumed activities.
|
||||||
|
for (ActivityResultStarter starter : starters) { |
||||||
|
if (starter.isActivityResumed()) { |
||||||
|
return starter; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return starters.get(0); |
||||||
|
} |
||||||
|
|
||||||
|
public interface ActivityResultStarter { |
||||||
|
boolean isActivityResumed(); |
||||||
|
|
||||||
|
void startActivityForResultWithCallback(Intent intent, int requestCode, |
||||||
|
ActivityResultCallback callback); |
||||||
|
} |
||||||
|
|
||||||
|
public interface ApplicationActivitiesProvider { |
||||||
|
/** |
||||||
|
* Get all created activities. You can use Application.ActivityLifecycleCallbacks |
||||||
|
* to track the activities that are between the onCreate and onDestroy stage. |
||||||
|
* |
||||||
|
* @return a list of created activities. |
||||||
|
*/ |
||||||
|
List<Activity> getActivities(); |
||||||
|
} |
||||||
|
|
||||||
|
public interface ActivityResultCallback { |
||||||
|
void onActivityResult(int resultCode, Intent result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Helper class for Activities that implement the ActivityResultStarter interface. |
||||||
|
*/ |
||||||
|
public static class ActivityStarterHelper { |
||||||
|
private SparseArray<ActivityResultCallback> activityResultCallbacks |
||||||
|
= new SparseArray<>(); |
||||||
|
private boolean isResumed = false; |
||||||
|
|
||||||
|
public void onResume() { |
||||||
|
isResumed = true; |
||||||
|
} |
||||||
|
|
||||||
|
public void onPause() { |
||||||
|
isResumed = false; |
||||||
|
} |
||||||
|
|
||||||
|
public boolean isActivityResumed() { |
||||||
|
return isResumed; |
||||||
|
} |
||||||
|
|
||||||
|
public void startActivityForResult(Activity activity, Intent intent, |
||||||
|
int requestCode, ActivityResultCallback callback) { |
||||||
|
if (activityResultCallbacks.indexOfKey(requestCode) >= 0) { |
||||||
|
throw new IllegalArgumentException("requestCode " + requestCode + " already used"); |
||||||
|
} |
||||||
|
if (intent.resolveActivity(activity.getPackageManager()) != null) { |
||||||
|
activity.startActivityForResult(intent, requestCode); |
||||||
|
activityResultCallbacks.put(requestCode, callback); |
||||||
|
} else { |
||||||
|
Logger.e(TAG, "Can't start activity for result, intent does not resolve."); |
||||||
|
callback.onActivityResult(Activity.RESULT_CANCELED, null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public boolean onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
if (activityResultCallbacks.indexOfKey(requestCode) >= 0) { |
||||||
|
ActivityResultHelper.ActivityResultCallback callback = |
||||||
|
activityResultCallbacks.get(requestCode); |
||||||
|
activityResultCallbacks.delete(requestCode); |
||||||
|
|
||||||
|
callback.onActivityResult(resultCode, data); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,79 +0,0 @@ |
|||||||
package org.floens.chan.ui.controller; |
|
||||||
|
|
||||||
import android.app.Activity; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.view.View; |
|
||||||
import android.widget.Button; |
|
||||||
import android.widget.TextView; |
|
||||||
|
|
||||||
import org.floens.chan.R; |
|
||||||
import org.floens.chan.controller.Controller; |
|
||||||
import org.floens.chan.core.storage.Storage; |
|
||||||
|
|
||||||
import javax.inject.Inject; |
|
||||||
|
|
||||||
import static org.floens.chan.Chan.inject; |
|
||||||
|
|
||||||
public class StorageSetupController extends Controller implements View.OnClickListener { |
|
||||||
private static final int OPEN_TREE_INTENT_RESULT_ID = 101; |
|
||||||
|
|
||||||
private static final String TAG = "StorageSetupController"; |
|
||||||
|
|
||||||
@Inject |
|
||||||
private Storage storage; |
|
||||||
|
|
||||||
private TextView text; |
|
||||||
private Button button; |
|
||||||
|
|
||||||
public StorageSetupController(Context context) { |
|
||||||
super(context); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onCreate() { |
|
||||||
super.onCreate(); |
|
||||||
|
|
||||||
inject(this); |
|
||||||
|
|
||||||
// Navigation
|
|
||||||
navigation.setTitle(R.string.storage_setup_screen); |
|
||||||
|
|
||||||
// View inflation
|
|
||||||
view = inflateRes(R.layout.controller_storage_setup); |
|
||||||
|
|
||||||
// View binding
|
|
||||||
text = view.findViewById(R.id.text); |
|
||||||
button = view.findViewById(R.id.button); |
|
||||||
|
|
||||||
// View setup
|
|
||||||
button.setOnClickListener(this); |
|
||||||
|
|
||||||
updateName(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onClick(View v) { |
|
||||||
if (v == button) { |
|
||||||
requestTree(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void requestTree() { |
|
||||||
Intent i = storage.getOpenTreeIntent(); |
|
||||||
((Activity) context).startActivityForResult(i, OPEN_TREE_INTENT_RESULT_ID); |
|
||||||
updateName(); |
|
||||||
} |
|
||||||
|
|
||||||
private void updateName() { |
|
||||||
text.setText(storage.currentStorageName()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) { |
|
||||||
if (requestCode == OPEN_TREE_INTENT_RESULT_ID && resultCode == Activity.RESULT_OK) { |
|
||||||
storage.handleOpenTreeIntent(data.getData()); |
|
||||||
updateName(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="match_parent"> |
|
||||||
|
|
||||||
<LinearLayout |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:orientation="vertical" |
|
||||||
android:padding="16dp"> |
|
||||||
|
|
||||||
<TextView |
|
||||||
android:id="@+id/text" |
|
||||||
android:layout_width="match_parent" |
|
||||||
android:layout_height="wrap_content" /> |
|
||||||
|
|
||||||
<Button |
|
||||||
android:id="@+id/button" |
|
||||||
android:layout_width="wrap_content" |
|
||||||
android:layout_height="wrap_content" |
|
||||||
android:text="open" /> |
|
||||||
|
|
||||||
</LinearLayout> |
|
||||||
|
|
||||||
</ScrollView> |
|
Loading…
Reference in new issue