diff --git a/app/build.gradle b/app/build.gradle index 683e534..187205c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,14 +6,13 @@ def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { - compileSdkVersion 33 - buildToolsVersion '31.0.0' + compileSdk = 34 defaultConfig { applicationId "com.maary.shareas" minSdkVersion 29 - targetSdkVersion 32 + targetSdkVersion 34 versionCode 4 - versionName "2.2_beta_230625" + versionName "2.5-alpha-240411" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildFeatures { @@ -93,21 +92,28 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.core:core-ktx:1.10.1' - implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.core:core-ktx:1.12.0' + implementation 'androidx.activity:activity:1.8.2' + implementation "androidx.datastore:datastore-preferences:1.0.0" + // optional - RxJava2 support + implementation "androidx.datastore:datastore-preferences-rxjava2:1.0.0" + // optional - RxJava3 support + implementation "androidx.datastore:datastore-preferences-rxjava3:1.0.0" + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' // implementation 'com.hoko:hoko-blur:1.3.5' - implementation 'io.github.hokofly:hoko-blur:1.5.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'io.github.hokofly:hoko-blur:1.5.3' + implementation 'com.google.android.material:material:1.11.0' - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1" - implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1" - implementation "androidx.activity:activity-ktx:1.7.2" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0" + implementation "androidx.activity:activity-ktx:1.8.2" - implementation 'com.github.bumptech.glide:glide:4.15.1' + implementation 'com.github.bumptech.glide:glide:4.16.0' //noinspection GradleCompatible implementation 'com.android.support:palette-v7:28.0.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 556aaf0..009822f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,57 +4,76 @@ - + + + + + + android:name=".activity.WelcomeActivity" + android:exported="true"> + + - - - - - - - - + + + + + + + + + + android:exported="false" + android:grantUriPermissions="true"> diff --git a/app/src/main/java/com/maary/shareas/HistoryActivityViewModel.kt b/app/src/main/java/com/maary/shareas/HistoryActivityViewModel.kt index fa5d917..24866c0 100644 --- a/app/src/main/java/com/maary/shareas/HistoryActivityViewModel.kt +++ b/app/src/main/java/com/maary/shareas/HistoryActivityViewModel.kt @@ -10,8 +10,10 @@ import android.database.Cursor import android.net.Uri import android.os.Build import android.os.Handler +import android.os.Looper import android.provider.MediaStore import android.util.Log +import androidx.annotation.RequiresApi import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData @@ -64,12 +66,14 @@ class HistoryActivityViewModel(application: Application) : AndroidViewModel(appl return uriList } + @RequiresApi(Build.VERSION_CODES.R) fun deleteImage(image: MediaStoreImage) { viewModelScope.launch { performDeleteImage(image) } } + @RequiresApi(Build.VERSION_CODES.R) fun deletePendingImage() { pendingDeleteImage?.let { image -> pendingDeleteImage = null @@ -250,7 +254,7 @@ class HistoryActivityViewModel(application: Application) : AndroidViewModel(appl val image = MediaStoreImage(id, displayName, dateModified, contentUri, width = width, height = height) - Log.v("WLAP", width.toString() + " " + height.toString()) + Log.v("WLAP", "$width $height") images += image // For debugging, we'll output the image objects we create to logcat. @@ -263,6 +267,7 @@ class HistoryActivityViewModel(application: Application) : AndroidViewModel(appl return images } + @RequiresApi(Build.VERSION_CODES.R) private suspend fun performDeleteImage(image: MediaStoreImage) { withContext(Dispatchers.IO) { try { @@ -342,7 +347,7 @@ private fun ContentResolver.registerObserver( uri: Uri, observer: (selfChange: Boolean) -> Unit ): ContentObserver { - val contentObserver = object : ContentObserver(Handler()) { + val contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) { override fun onChange(selfChange: Boolean) { observer(selfChange) } diff --git a/app/src/main/java/com/maary/shareas/QSTileService.kt b/app/src/main/java/com/maary/shareas/QSTileService.kt index 8fbe428..81798f6 100644 --- a/app/src/main/java/com/maary/shareas/QSTileService.kt +++ b/app/src/main/java/com/maary/shareas/QSTileService.kt @@ -1,8 +1,11 @@ package com.maary.shareas +import android.app.PendingIntent import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.os.Build import android.service.quicksettings.TileService +import com.maary.shareas.activity.HistoryActivity class QSTileService: TileService() { override fun onClick() { @@ -11,6 +14,17 @@ class QSTileService: TileService() { val intent = Intent(this, HistoryActivity::class.java) .addFlags(FLAG_ACTIVITY_NEW_TASK) - startActivityAndCollapse(intent) + val pendingIntent = PendingIntent.getActivity( + this, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE){ + startActivityAndCollapse(pendingIntent) + }else { + startActivityAndCollapse(intent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/WallpaperAdapter.java b/app/src/main/java/com/maary/shareas/WallpaperAdapter.java deleted file mode 100644 index 55e85ce..0000000 --- a/app/src/main/java/com/maary/shareas/WallpaperAdapter.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.maary.shareas; - -import android.content.Context; -import android.media.Image; -import android.widget.ArrayAdapter; - -import androidx.annotation.NonNull; - -public class WallpaperAdapter extends ArrayAdapter { - public WallpaperAdapter(@NonNull Context context, int resource) { - super(context, resource); - } -} diff --git a/app/src/main/java/com/maary/shareas/HistoryActivity.kt b/app/src/main/java/com/maary/shareas/activity/HistoryActivity.kt similarity index 95% rename from app/src/main/java/com/maary/shareas/HistoryActivity.kt rename to app/src/main/java/com/maary/shareas/activity/HistoryActivity.kt index 2337ea6..b90c177 100644 --- a/app/src/main/java/com/maary/shareas/HistoryActivity.kt +++ b/app/src/main/java/com/maary/shareas/activity/HistoryActivity.kt @@ -1,4 +1,4 @@ -package com.maary.shareas +package com.maary.shareas.activity import android.Manifest import android.app.Activity @@ -19,19 +19,23 @@ import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import android.widget.ImageView import androidx.activity.result.IntentSenderRequest import androidx.activity.viewModels +import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.view.* import androidx.databinding.DataBindingUtil -import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.google.android.material.color.DynamicColors import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.maary.shareas.HistoryActivityViewModel +import com.maary.shareas.MediaStoreImage +import com.maary.shareas.R +import com.maary.shareas.helper.Util import com.maary.shareas.databinding.ActivityHistoryBinding import kotlinx.coroutines.* @@ -101,11 +105,6 @@ class HistoryActivity : AppCompatActivity(){ } -// val galleryAdapter = GalleryAdapter ( -// onClick = {image -> openImage(image)}, -// onLongClick = {image -> deleteImage(image)} -// ) - binding.gallery.also { view -> view.layoutManager = GridLayoutManager(this, 3) view.adapter = galleryAdapter @@ -202,6 +201,7 @@ class HistoryActivity : AppCompatActivity(){ } } + @RequiresApi(Build.VERSION_CODES.R) override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK && requestCode == DELETE_PERMISSION_REQUEST) { @@ -256,9 +256,18 @@ class HistoryActivity : AppCompatActivity(){ if (!haveStoragePermission()) { val permissions = arrayOf( Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE +// Manifest.permission.WRITE_EXTERNAL_STORAGE, ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ + val permissionsT = arrayOf( + Manifest.permission.READ_MEDIA_IMAGES, +// Manifest.permission.WRITE_EXTERNAL_STORAGE, + ) + ActivityCompat.requestPermissions(this, permissionsT, READ_EXTERNAL_STORAGE_REQUEST) + }else{ ActivityCompat.requestPermissions(this, permissions, READ_EXTERNAL_STORAGE_REQUEST) + } } } @@ -358,7 +367,6 @@ class HistoryActivity : AppCompatActivity(){ Glide.with(holder.imageView) .load(mediaStoreImage.contentUri) - .thumbnail(0.33f) .centerCrop() .into(holder.imageView) } diff --git a/app/src/main/java/com/maary/shareas/MainActivity.java b/app/src/main/java/com/maary/shareas/activity/MainActivity.java similarity index 78% rename from app/src/main/java/com/maary/shareas/MainActivity.java rename to app/src/main/java/com/maary/shareas/activity/MainActivity.java index bc773ad..758f84c 100644 --- a/app/src/main/java/com/maary/shareas/MainActivity.java +++ b/app/src/main/java/com/maary/shareas/activity/MainActivity.java @@ -1,4 +1,4 @@ -package com.maary.shareas; +package com.maary.shareas.activity; import static com.google.android.material.slider.LabelFormatter.LABEL_GONE; @@ -10,7 +10,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.ColorStateList; import android.graphics.Bitmap; @@ -21,7 +20,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; +import android.os.Environment; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -33,10 +32,7 @@ import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; -import android.widget.Toast; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; @@ -46,7 +42,6 @@ import androidx.appcompat.view.menu.ActionMenuItemView; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.view.WindowCompat; @@ -61,10 +56,15 @@ import com.google.android.material.snackbar.Snackbar; import com.hoko.blur.HokoBlur; import com.hoko.blur.task.AsyncBlurTask; +import com.maary.shareas.helper.PreferencesHelper; +import com.maary.shareas.R; +import com.maary.shareas.helper.Util; +import com.maary.shareas.helper.Util_Files; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -72,19 +72,6 @@ public class MainActivity extends AppCompatActivity { static final int MENU_RESET = 0; //请求权限 - private final ActivityResultLauncher requestPermissionLauncher = - registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { - if (isGranted) { - recreate(); - } - if (!isGranted) { - Toast.makeText(this, R.string.no_permission, Toast.LENGTH_SHORT).show(); - SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.enabled_history_key), false); - editor.apply(); - } - }); Bitmap bitmap; Bitmap processed; Bitmap blurProcessed; @@ -105,9 +92,10 @@ public class MainActivity extends AppCompatActivity { Palette.Swatch dominant; Palette.Swatch muted ; + Intent intent; + Snackbar snackbarReturnHome; - //TODO:change later - int state = 0; + @SuppressLint("RestrictedApi") @RequiresApi(api = Build.VERSION_CODES.S) @@ -117,24 +105,20 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); WindowCompat.setDecorFitsSystemWindows(getWindow(), false); - SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE); + PreferencesHelper preferencesHelper = new PreferencesHelper(this); - if (sharedPreferences.contains(getString(R.string.device_height))) { - device_height = sharedPreferences.getInt(getString(R.string.device_height), - Util.getDeviceBounds(MainActivity.this).y); - device_width = sharedPreferences.getInt(getString(R.string.device_width), - Util.getDeviceBounds(MainActivity.this).x); - } else { + device_height = preferencesHelper.getHeight(); + if (device_height == -1){ Point deviceBounds = Util.getDeviceBounds(MainActivity.this); device_height = deviceBounds.y; device_width = deviceBounds.x; - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putInt(getString(R.string.device_height), device_height); - editor.putInt(getString(R.string.device_width), device_width); - editor.apply(); + + preferencesHelper.setWidthAndHeight(device_width, device_height); + } else { + device_width = preferencesHelper.getWidth(); } - Intent intent = getIntent(); + intent = getIntent(); String action = intent.getAction(); String type = intent.getType(); final WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext()); @@ -142,22 +126,10 @@ protected void onCreate(Bundle savedInstanceState) { try { if (Intent.ACTION_SEND.equals(action) && type != null) { if (type.startsWith("image/")) { - bitmap = Util.getBitmap(intent, MainActivity.this);}} - else { - if (ContextCompat.checkSelfPermission( - getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); - } - if (wallpaperManager.getWallpaperInfo() == null) { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - bitmap = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap(); - } else { - Toast.makeText(getApplicationContext(), R.string.cannot_getpermission_lacking_permission, Toast.LENGTH_SHORT).show(); - } - } else { - Toast.makeText(getApplicationContext(), R.string.cannot_get_livewallpaper, Toast.LENGTH_SHORT).show(); + bitmap = Util.getBitmap(intent, MainActivity.this); } } + if (bitmap != null) { //Parent layout ConstraintLayout container = findViewById(R.id.container); @@ -216,7 +188,6 @@ protected void onCreate(Bundle savedInstanceState) { processed = bitmap; raw = bitmap; -// bottomAppBar.getMenu().getItem(MENU_RESET).setEnabled(false); imageView.setImageBitmap(bitmap); imageView.setOnClickListener(v -> { @@ -272,15 +243,8 @@ protected void onCreate(Bundle savedInstanceState) { }); - //如果 SharedPreferences 里没有关于是否保存图像历史的偏好就询问是否保存 - if (!sharedPreferences.contains(getString(R.string.enabled_history_key))) { - AlertDialog dialog_history = saveHistoryDialog(); - dialog_history.show(); - } - //setup the fab click listener fab.setOnClickListener(view -> { - //TODO: temp comment if (isVertical) { int start = verticalScrollView.getScrollY(); cord = new Rect(0, start, device_width, start + device_height); @@ -295,22 +259,12 @@ protected void onCreate(Bundle savedInstanceState) { }); fab.setOnLongClickListener(view -> { - - if (sharedPreferences.getBoolean(getString(R.string.enabled_history_key), true)) { - Toast.makeText(this, "save wallpaper", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "not save wallpaper", Toast.LENGTH_SHORT).show(); - } - cord = null; AlertDialog dialog = builder.create(); dialog.show(); return false; }); - //TODO:JUST FOR TEST - Util_Files.getWallpapersList(); - //TODO:ADD zoom (if possible WallpaperManager.OnColorsChangedListener wallpaperChangedListener = new WallpaperManager.OnColorsChangedListener() { @@ -336,7 +290,7 @@ public void onColorsChanged(@Nullable WallpaperColors colors, int which) { AlertDialog dialog; dialog = createSliderDialog(); - dialog.getWindow() + Objects.requireNonNull(dialog.getWindow()) .clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); dialog.getWindow().setGravity(Gravity.BOTTOM); dialog.setCancelable(false); @@ -460,7 +414,6 @@ public void onColorsChanged(@Nullable WallpaperColors colors, int which) { .radius((int) value) .sampleFactor(1.0f) .forceCopy(true) - .needUpscale(true) .asyncBlur(toProcess, new AsyncBlurTask.Callback() { @Override public void onBlurSuccess(Bitmap bitmap) { @@ -542,48 +495,38 @@ public void onBlurFailed(Throwable error) { builder.setItems(options, (dialog, which) -> executorService.execute(() -> { try { - - //若已经选择保存选项,弹出「设置图片为」选项之前保存图片 - if (wallpaperManager.getWallpaperInfo() == null) { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + if (new PreferencesHelper(this).getSettingsHistory()){ + //若已经选择保存选项,弹出「设置图片为」选项之前保存图片 + if (checkPermission()) { Bitmap currentWallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap(); Util_Files.saveWallpaper(currentWallpaper, this); -// saveWallpaper(currentWallpaper); - } else { - Toast.makeText(getApplicationContext(), R.string.no_permission, Toast.LENGTH_SHORT).show(); + }else { + Snackbar.make(container, R.string.no_permission, Snackbar.LENGTH_SHORT) + .show(); } } switch (which) { case 0 -> { - state = 0; wallpaperManager.setBitmap(bitmap, cord, true, WallpaperManager.FLAG_SYSTEM); } case 1 -> { - state = 1; - wallpaperManager.setBitmap(bitmap, cord, true, WallpaperManager.FLAG_LOCK); } case 2 -> { - state = 2; if (applyEditToLock) { wallpaperManager.setBitmap(bitmap, cord, true, WallpaperManager.FLAG_LOCK); } else { wallpaperManager.setBitmap(raw, cord, true, WallpaperManager.FLAG_LOCK); } - - Log.v("WALLP", "LOCK SET"); - if (applyEditToHome) { wallpaperManager.setBitmap(bitmap, cord, true, WallpaperManager.FLAG_SYSTEM); } else { wallpaperManager.setBitmap(raw, cord, true, WallpaperManager.FLAG_SYSTEM); } - Log.v("WALLP", "HOME SET"); } //应对定制 Rom(如 Color OS)可能存在的魔改导致 "WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM" 参数失效的情况。 case 3 -> { - state = 3; shareBitmap(bitmap, getApplicationContext()); } default -> throw new IllegalStateException("Unexpected value: " + which); @@ -599,8 +542,20 @@ public void onBlurFailed(Throwable error) { } catch (IOException e) { throw new RuntimeException(e); } + } - + private Boolean checkPermission(){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Boolean hasStoragePermission = ContextCompat.checkSelfPermission( + getApplicationContext(), + Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED; + Boolean hasExternalStorageManagerPermission = Environment.isExternalStorageManager(); + return hasStoragePermission && hasExternalStorageManagerPermission; + } else { + return ContextCompat.checkSelfPermission( + getApplicationContext(), + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } } private void returnToHomeScreen() { @@ -624,46 +579,7 @@ private AlertDialog createSliderDialog() { return builder.create(); } - //询问是否需要保存壁纸历史记录 - private AlertDialog saveHistoryDialog() { - MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); - builder.setMessage(R.string.dialog_wallpaper_history) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> { - SharedPreferences sharedPreferences = getSharedPreferences(getString(R.string.preference_file_key), MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.enabled_history_key), true); - editor.apply(); - - //如果需要 - if (sharedPreferences.getBoolean(getString(R.string.enabled_history_key), false)) { - if (ContextCompat.checkSelfPermission( - getApplicationContext(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); - } - if (ContextCompat.checkSelfPermission( - getApplicationContext(), Manifest.permission.MANAGE_MEDIA) != PackageManager.PERMISSION_GRANTED) { - Intent intent = null; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - intent = new Intent(Settings.ACTION_REQUEST_MANAGE_MEDIA); - } - startActivity(intent); - } - } - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - Toast.makeText(getApplicationContext(), R.string.disabled_history_lacking_permission, Toast.LENGTH_SHORT).show(); - editor.putBoolean(getString(R.string.enabled_history_key), false); - editor.apply(); - } - }) - .setNegativeButton(R.string.cancel, (dialogInterface, i) -> { - SharedPreferences sharedPreferences = getPreferences(Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putBoolean(getString(R.string.enabled_history_key), false); - editor.apply(); - }); - return builder.create(); - } private void shareBitmap(@NonNull Bitmap bitmap, Context context) { //---Save bitmap to external cache directory---// diff --git a/app/src/main/java/com/maary/shareas/activity/StartActivity.kt b/app/src/main/java/com/maary/shareas/activity/StartActivity.kt new file mode 100644 index 0000000..d1a1ff7 --- /dev/null +++ b/app/src/main/java/com/maary/shareas/activity/StartActivity.kt @@ -0,0 +1,213 @@ +package com.maary.shareas.activity + +import android.Manifest +import android.app.WallpaperManager +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.os.ParcelFileDescriptor +import android.view.View +import android.view.ViewGroup.MarginLayoutParams +import androidx.activity.enableEdgeToEdge +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import androidx.core.graphics.drawable.toBitmap +import androidx.core.graphics.drawable.toBitmapOrNull +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.databinding.DataBindingUtil +import androidx.lifecycle.lifecycleScope +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.color.DynamicColors +import com.maary.shareas.R +import com.maary.shareas.databinding.ActivityStartBinding +import com.maary.shareas.helper.PreferencesHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import java.io.IOException + +class StartActivity : AppCompatActivity(){ + + private lateinit var binding: ActivityStartBinding + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + DynamicColors.applyToActivityIfAvailable(this) + + binding = DataBindingUtil.setContentView(this, R.layout.activity_start) + + runBlocking { + val preferencesHelper = PreferencesHelper(applicationContext) + val settingsFinished = preferencesHelper.getSettingsFinished().first() + if (!settingsFinished){ + val intent = Intent(applicationContext, WelcomeActivity::class.java) + startActivity(intent) + finish() + } + } + + val pickerBottomSheetBehavior = BottomSheetBehavior.from(binding.pickerBottomSheet) + pickerBottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED + + ViewCompat.setOnApplyWindowInsetsListener(binding.buttonPicker) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + bottomMargin = systemBars.bottom + } + + // some dirty hack + pickerBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED + pickerBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + + insets + } + + + + val wallpaperManager: WallpaperManager = WallpaperManager.getInstance(this) + val homePFD:ParcelFileDescriptor? + var lockPFD:ParcelFileDescriptor? + val homeBitmap: Bitmap + var lockBitmap: Bitmap? + + if (checkPermission()){ + homePFD = wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_SYSTEM) + if (homePFD != null) { + lockPFD = wallpaperManager.getWallpaperFile(WallpaperManager.FLAG_LOCK) + if (lockPFD == null){ + lockPFD = homePFD + } + + homeBitmap = BitmapFactory.decodeFileDescriptor(homePFD.fileDescriptor) + lockBitmap = BitmapFactory.decodeFileDescriptor(lockPFD.fileDescriptor) + + }else{ + homeBitmap = (wallpaperManager.getBuiltInDrawable(WallpaperManager.FLAG_SYSTEM)).toBitmap() + lockBitmap = (wallpaperManager.getBuiltInDrawable(WallpaperManager.FLAG_LOCK)).toBitmapOrNull() + if (lockBitmap == null){ + lockBitmap = homeBitmap + } + } + binding.homeContainer.setImageBitmap(homeBitmap) + binding.lockContainer.setImageBitmap(lockBitmap) + + var isHomeSaved = false + var isLockSaved = false + + lifecycleScope.launch { + isHomeSaved = saveBitmapAsync(homeBitmap, "home") + isLockSaved = lockBitmap?.let { saveBitmapAsync(it, "lock") } == true + } + + binding.homeContainer.setOnClickListener { + while (!isHomeSaved){} + shareImg("home") + } + + binding.lockContainer.setOnClickListener { + if (lockBitmap != null) { +// shareBitmap(lockBitmap) + while (!isLockSaved){} + shareImg("lock") + } + } + + } else { + binding.systemWallpContainer.visibility = View.INVISIBLE + } + + val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri-> + if (uri != null) { + val intent = Intent(application, MainActivity::class.java).apply { + action = Intent.ACTION_SEND + setDataAndType(uri, "image/*") + putExtra("mimeType", "image/*") + putExtra(Intent.EXTRA_STREAM, uri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + startActivity(intent) + } + + } + + binding.buttonPicker.setOnClickListener { + pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } + + + } + + private fun checkPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ + (Environment.isExternalStorageManager() + && ContextCompat.checkSelfPermission(application, + Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED) + } else { + ContextCompat.checkSelfPermission(application, + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED + + } + } + + // Define a suspend function to save the bitmap asynchronously + private suspend fun saveBitmapAsync(bitmap: Bitmap, name: String): Boolean { + return withContext(Dispatchers.IO) { + val cachePath = File(externalCacheDir, "my_images/") + cachePath.mkdirs() + + //create png file + val file = File(cachePath, "$name.png") + try { + val fileOutputStream = FileOutputStream(file) + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream) + fileOutputStream.flush() + fileOutputStream.close() + return@withContext true // Return true if saving succeeds + } catch (e: IOException) { + e.printStackTrace() + return@withContext false // Return false if saving fails + } + } + } + + private fun shareImg(name: String) { + + //---Save bitmap to external cache directory---// + //get cache directory + val cachePath = File(externalCacheDir, "my_images/") + cachePath.mkdirs() + + //create png file + val file = File(cachePath, "$name.png") + + //---Share File---// + //get file uri + val myImageFileUri = + FileProvider.getUriForFile(this, applicationContext.packageName + ".provider", file) + + val intent = Intent(application, MainActivity::class.java).apply { + action = Intent.ACTION_SEND + setDataAndType(myImageFileUri, "image/*") + putExtra("mimeType", "image/*") + putExtra(Intent.EXTRA_STREAM, myImageFileUri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + startActivity(intent) + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/activity/WelcomeActivity.kt b/app/src/main/java/com/maary/shareas/activity/WelcomeActivity.kt new file mode 100644 index 0000000..6902dcf --- /dev/null +++ b/app/src/main/java/com/maary/shareas/activity/WelcomeActivity.kt @@ -0,0 +1,63 @@ +package com.maary.shareas.activity + +import android.os.Bundle +import androidx.activity.addCallback +import androidx.activity.enableEdgeToEdge +import androidx.databinding.DataBindingUtil +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.viewpager2.adapter.FragmentStateAdapter +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.color.DynamicColors +import com.maary.shareas.R +import com.maary.shareas.databinding.ActivityWelcomeBinding +import com.maary.shareas.fragment.WelcomeFinishFragment +import com.maary.shareas.fragment.WelcomeHistoryFragment +import com.maary.shareas.fragment.WelcomeSystemFragment + +private const val NUM_PAGES = 3 +class WelcomeActivity : FragmentActivity() { + + private lateinit var viewPager: ViewPager2 + private lateinit var binding: ActivityWelcomeBinding + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + DynamicColors.applyToActivityIfAvailable(this) + + binding = DataBindingUtil.setContentView(this, R.layout.activity_welcome) + + viewPager = binding.pager + val pagerAdapter = WelcomeFragmentAdapter(this) + viewPager.adapter = pagerAdapter + viewPager.isUserInputEnabled = false + + onBackPressedDispatcher.addCallback { + if (viewPager.currentItem == 0) { + // If the user is currently looking at the first step, allow the system to handle + // the Back button. This calls finish() on this activity and pops the back stack. + finish() + } else { + // Otherwise, select the previous step. + viewPager.currentItem -= 1 + } + } + + } + + + + + private inner class WelcomeFragmentAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { + override fun getItemCount(): Int = NUM_PAGES + + override fun createFragment(position: Int): Fragment { + return when (position) { + 0 -> WelcomeHistoryFragment() + 1 -> WelcomeSystemFragment() + else -> WelcomeFinishFragment() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/fragment/WelcomeFinishFragment.kt b/app/src/main/java/com/maary/shareas/fragment/WelcomeFinishFragment.kt new file mode 100644 index 0000000..80cab0c --- /dev/null +++ b/app/src/main/java/com/maary/shareas/fragment/WelcomeFinishFragment.kt @@ -0,0 +1,108 @@ +package com.maary.shareas.fragment + +import android.content.Intent +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.marginBottom +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.maary.shareas.helper.PreferencesHelper +import com.maary.shareas.R +import com.maary.shareas.activity.StartActivity +import com.maary.shareas.databinding.FragmentWelcomeFinishBinding +import kotlinx.coroutines.launch + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [WelcomeFinishFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class WelcomeFinishFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + private var _binding: FragmentWelcomeFinishBinding? = null + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentWelcomeFinishBinding.inflate(inflater, container, false) + // Inflate the layout for this fragment + val rootView = binding.root + ViewCompat.setOnApplyWindowInsetsListener(rootView.findViewById(R.id.fab_welcome_finish_next)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + if (bottomMargin < systemBars.bottom){ + bottomMargin = systemBars.bottom + v.marginBottom + } + } + rootView.findViewById(R.id.container_welcome_finish_appbar) + .setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } + rootView.findViewById(R.id.fab_welcome_finish_next).setOnClickListener { + lifecycleScope.launch { + val preferencesHelper = PreferencesHelper(requireContext()) + preferencesHelper.setSettingsFinished() + } + val intent = Intent(activity, StartActivity::class.java) + startActivity(intent) + requireActivity().finish() + } + binding.topAppBarWelcomeFinish.setNavigationOnClickListener { + val viewPager = requireActivity().findViewById(R.id.pager) + viewPager.setCurrentItem(viewPager.currentItem - 1, true) + } + return rootView + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment WelcomeFinishFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + WelcomeFinishFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/fragment/WelcomeHistoryFragment.kt b/app/src/main/java/com/maary/shareas/fragment/WelcomeHistoryFragment.kt new file mode 100644 index 0000000..017aaf0 --- /dev/null +++ b/app/src/main/java/com/maary/shareas/fragment/WelcomeHistoryFragment.kt @@ -0,0 +1,231 @@ +package com.maary.shareas.fragment + +import android.Manifest +import android.app.StatusBarManager +import android.content.ComponentName +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.drawable.Icon +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.provider.Settings +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.marginBottom +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.maary.shareas.helper.PreferencesHelper +import com.maary.shareas.QSTileService +import com.maary.shareas.R +import com.maary.shareas.databinding.FragmentWelcomeHistoryBinding +import kotlinx.coroutines.launch +import java.util.concurrent.Executor + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [WelcomeHistoryFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class WelcomeHistoryFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + private var _binding: FragmentWelcomeHistoryBinding? = null + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentWelcomeHistoryBinding.inflate(inflater, container, false) + val viewPager = requireActivity().findViewById(R.id.pager) + + // Inflate the layout for this fragment + val rootView = binding.root + rootView.findViewById(R.id.fab_welcome_history_next).setOnClickListener { + if (checkPermission()){ + lifecycleScope.launch { + PreferencesHelper(requireContext()).setSettingsHistory(true) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ + viewPager.setCurrentItem(viewPager.currentItem + 1, true) + }else{ + viewPager.setCurrentItem(viewPager.currentItem + 2, true) + } + } else{ + lifecycleScope.launch { + PreferencesHelper(requireContext()).setSettingsHistory(false) + } + viewPager.setCurrentItem(viewPager.currentItem + 2, true) + + } + } + binding.topAppBarWelcomeHistory.setNavigationOnClickListener { + activity?.onBackPressedDispatcher?.onBackPressed() + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU){ + binding.textWelcomeHistoryPermission2.visibility = View.GONE + binding.textWelcomeHistoryPermission1Edit.setText(R.string.read_external_storage) + } + + val requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + if (!Environment.isExternalStorageManager()) { + startActivity( + Intent( + Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, + Uri.parse("package:com.maary.shareas") + ) + ) + } + } + } + } + + binding.buttonWelcomeHistoryYes.setOnClickListener { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermissionLauncher.launch(Manifest.permission.READ_MEDIA_IMAGES) + } else { + requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + binding.buttonWelcomeHistoryNo.setOnClickListener { + lifecycleScope.launch { + PreferencesHelper(requireContext()).setSettingsHistory(false) + } + viewPager.setCurrentItem(viewPager.currentItem + 2, true) + } + return rootView + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + ViewCompat.setOnApplyWindowInsetsListener(view.findViewById(R.id.fab_welcome_history_next)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + if (bottomMargin < systemBars.bottom){ + bottomMargin = systemBars.bottom + v.marginBottom + } + } + view.findViewById(R.id.container_welcome_history_appbar) + .setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } + + } + + @RequiresApi(Build.VERSION_CODES.S) + override fun onResume() { + super.onResume() + if (checkPermission()){ + binding.textWelcomeHistoryPermission1.helperText = getText(R.string.permission_got) + binding.textWelcomeHistoryPermission2.helperText = getText(R.string.permission_got) + binding.buttonWelcomeHistoryNo.visibility = View.INVISIBLE + + binding.buttonWelcomeHistoryYes.text = getText(R.string.add_quick_tile) + binding.buttonWelcomeHistoryYes.setOnClickListener { + val statusBarManager = requireActivity().getSystemService(StatusBarManager::class.java) + val resultSuccessExecutor = Executor { + binding.buttonWelcomeHistoryYes.visibility = View.INVISIBLE + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + statusBarManager.requestAddTileService( + ComponentName( + requireContext(), + QSTileService::class.java + ), + getString(R.string.history_activity), + Icon.createWithResource( + requireContext(), + R.drawable.ic_history + ), + resultSuccessExecutor + ) { + } + } + } + }else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val hasStoragePermission = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED + val hasExternalStorageManagerPermission = Environment.isExternalStorageManager() + + if (hasExternalStorageManagerPermission) { + binding.textWelcomeHistoryPermission1.helperText = getText(R.string.permission_got) + } + + if (hasStoragePermission) { + binding.textWelcomeHistoryPermission2.helperText = getText(R.string.permission_got) + } + } + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment WelcomeHistoryFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + WelcomeHistoryFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } + + private fun checkPermission(): Boolean{ + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + val hasStoragePermission = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED + val hasExternalStorageManagerPermission = Environment.isExternalStorageManager() + hasStoragePermission && hasExternalStorageManagerPermission + } else { + ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/fragment/WelcomeSystemFragment.kt b/app/src/main/java/com/maary/shareas/fragment/WelcomeSystemFragment.kt new file mode 100644 index 0000000..a4f8184 --- /dev/null +++ b/app/src/main/java/com/maary/shareas/fragment/WelcomeSystemFragment.kt @@ -0,0 +1,128 @@ +package com.maary.shareas.fragment + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.provider.MediaStore +import android.provider.Settings +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.RequiresApi +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.marginBottom +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.maary.shareas.R +import com.maary.shareas.databinding.FragmentWelcomeSystemBinding + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [WelcomeSystemFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class WelcomeSystemFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + private var _binding: FragmentWelcomeSystemBinding? = null + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentWelcomeSystemBinding.inflate(inflater, container, false) + val viewPager = requireActivity().findViewById(R.id.pager) + + // Inflate the layout for this fragment + val rootView = binding.root + ViewCompat.setOnApplyWindowInsetsListener(rootView.findViewById(R.id.fab_welcome_system_next)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updateLayoutParams { + if (bottomMargin < systemBars.bottom){ + bottomMargin = systemBars.bottom + v.marginBottom + } + } + rootView.findViewById(R.id.container_welcome_system_appbar) + .setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } + rootView.findViewById(R.id.fab_welcome_system_next).setOnClickListener { + viewPager.setCurrentItem(viewPager.currentItem + 1, true) + } + + binding.topAppBarWelcomeSystem.setNavigationOnClickListener { + viewPager.setCurrentItem(viewPager.currentItem - 1, true) + } + + binding.buttonWelcomeSystemYes.setOnClickListener { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val intent = Intent(Settings.ACTION_REQUEST_MANAGE_MEDIA) + startActivity(intent) + + } + } + binding.buttonWelcomeSystemNo.setOnClickListener { + viewPager.setCurrentItem(viewPager.currentItem + 1, true) + } + + return rootView + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + @RequiresApi(Build.VERSION_CODES.S) + override fun onResume() { + super.onResume() + if (MediaStore.canManageMedia(requireContext())) { + binding.buttonWelcomeSystemNo.visibility = View.INVISIBLE + binding.buttonWelcomeSystemYes.visibility = View.INVISIBLE + binding.textWelcomeSystemPermission1.helperText = getText(R.string.permission_got) + } + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment WelcomeSystemFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + WelcomeSystemFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/maary/shareas/helper/PreferenceHelper.kt b/app/src/main/java/com/maary/shareas/helper/PreferenceHelper.kt new file mode 100644 index 0000000..43d0f6f --- /dev/null +++ b/app/src/main/java/com/maary/shareas/helper/PreferenceHelper.kt @@ -0,0 +1,99 @@ +package com.maary.shareas.helper + +import android.content.Context +import android.util.Log +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.* +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import com.maary.shareas.R +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking + +val Context.dataStore: DataStore by preferencesDataStore( + name = "settings", + produceMigrations = { context -> + listOf(SharedPreferencesMigration(context, context.getString(R.string.preference_file_key))) + }) + +class PreferencesHelper(context: Context) { + + private val dataStore = context.dataStore + + companion object { + val SETTINGS_HISTORY = booleanPreferencesKey("enabled_history_key") + val SETTINGS_FINISHED = booleanPreferencesKey("SETTING_FINISHED") + var DEVICE_HEIGHT= intPreferencesKey("device_height") + var DEVICE_WIDTH = intPreferencesKey("device_width") + // Add more keys here as needed + } + + fun getSettingsFinished(): Flow { + return dataStore.data + .map { preferences -> + Log.v("PH", preferences[SETTINGS_FINISHED].toString()) + + preferences[SETTINGS_FINISHED] ?: false + } + } + + suspend fun setSettingsFinished(){ + dataStore.edit { settings -> + settings[SETTINGS_FINISHED] = true + } + } + + suspend fun setSettingsHistory(boolean: Boolean) { + dataStore.edit { settings -> + settings[SETTINGS_HISTORY] = boolean + } + } + + private fun getSettingsHistoryFlow(): Flow { + return dataStore.data + .map { preferences -> + preferences[SETTINGS_HISTORY] ?: false + } + } + + fun getSettingsHistory(): Boolean = runBlocking { + getSettingsHistoryFlow().first() + } + + private fun getHeightFlow(): Flow { + return dataStore.data + .map { preferences -> + preferences[DEVICE_HEIGHT] ?: -1 + } + } + + private fun getWidthFlow(): Flow { + return dataStore.data + .map { preferences -> + preferences[DEVICE_WIDTH] ?: -1 + } + } + + fun getHeight(): Int = runBlocking { + getHeightFlow().first() + } + + fun getWidth(): Int = runBlocking { + getWidthFlow().first() + } + + fun setWidthAndHeight(width: Int, height: Int) = runBlocking { + dataStore.edit { settings -> + settings[DEVICE_HEIGHT] = height + settings[DEVICE_WIDTH] = width + } + } + + + + +} diff --git a/app/src/main/java/com/maary/shareas/Util.java b/app/src/main/java/com/maary/shareas/helper/Util.java similarity index 89% rename from app/src/main/java/com/maary/shareas/Util.java rename to app/src/main/java/com/maary/shareas/helper/Util.java index 9345a92..7fbec3e 100644 --- a/app/src/main/java/com/maary/shareas/Util.java +++ b/app/src/main/java/com/maary/shareas/helper/Util.java @@ -1,12 +1,14 @@ -package com.maary.shareas; +package com.maary.shareas.helper; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.ImageDecoder; import android.graphics.Paint; import android.graphics.Point; import android.net.Uri; @@ -16,13 +18,15 @@ import android.view.WindowMetrics; import java.io.IOException; +import java.io.InputStream; public class Util { public static Bitmap getBitmap(Intent intent, Context context) throws IOException { Uri imageUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); if (imageUri != null) { - return MediaStore.Images.Media.getBitmap(context.getContentResolver(), imageUri); + InputStream inputStream = context.getContentResolver().openInputStream(imageUri); + return BitmapFactory.decodeStream(inputStream); } else return null; } diff --git a/app/src/main/java/com/maary/shareas/Util_Files.java b/app/src/main/java/com/maary/shareas/helper/Util_Files.java similarity index 84% rename from app/src/main/java/com/maary/shareas/Util_Files.java rename to app/src/main/java/com/maary/shareas/helper/Util_Files.java index c49619a..ea73885 100644 --- a/app/src/main/java/com/maary/shareas/Util_Files.java +++ b/app/src/main/java/com/maary/shareas/helper/Util_Files.java @@ -1,4 +1,4 @@ -package com.maary.shareas; +package com.maary.shareas.helper; import android.app.Activity; import android.content.ContentResolver; @@ -39,7 +39,9 @@ public static void saveWallpaper(Bitmap bitmap, Activity activity){ uri = contentResolver.insert(contentUri, contentValues); try { + assert uri != null; final OutputStream outputStream = contentResolver.openOutputStream(uri); + assert outputStream != null; if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) { throw new IOException("failed to save bitmap"); } @@ -48,15 +50,4 @@ public static void saveWallpaper(Bitmap bitmap, Activity activity){ e.printStackTrace(); } } - - public static Uri getWallpapersList(){ - ArrayList uriArrayList = new ArrayList<>(); - File file = new File(customDir); - - Uri collection; - collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); - System.out.println(collection); - - return collection; - } } diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 0000000..075e95d --- /dev/null +++ b/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_history_foreground.xml b/app/src/main/res/drawable/ic_history_foreground.xml new file mode 100644 index 0000000..0b51f13 --- /dev/null +++ b/app/src/main/res/drawable/ic_history_foreground.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_info.xml b/app/src/main/res/drawable/ic_info.xml new file mode 100644 index 0000000..ef3a1fe --- /dev/null +++ b/app/src/main/res/drawable/ic_info.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_next.xml b/app/src/main/res/drawable/ic_next.xml new file mode 100644 index 0000000..77e4f24 --- /dev/null +++ b/app/src/main/res/drawable/ic_next.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/img_history.xml b/app/src/main/res/drawable/img_history.xml new file mode 100644 index 0000000..1e4711e --- /dev/null +++ b/app/src/main/res/drawable/img_history.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/place_holder.png b/app/src/main/res/drawable/place_holder.png new file mode 100644 index 0000000..009ec96 Binary files /dev/null and b/app/src/main/res/drawable/place_holder.png differ diff --git a/app/src/main/res/layout/activity_history.xml b/app/src/main/res/layout/activity_history.xml index 0122292..375d0a3 100644 --- a/app/src/main/res/layout/activity_history.xml +++ b/app/src/main/res/layout/activity_history.xml @@ -7,7 +7,7 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".activity.MainActivity"> + android:src="@drawable/ic_no_image" + android:contentDescription="@string/no_history" /> + tools:context=".activity.MainActivity"> + + + + + + + + + + + + + + + + + + + + + + +