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